aboutsummaryrefslogtreecommitdiff
path: root/arch/sh/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/sh/drivers
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/sh/drivers')
-rw-r--r--arch/sh/drivers/Makefile7
-rw-r--r--arch/sh/drivers/dma/Kconfig55
-rw-r--r--arch/sh/drivers/dma/Makefile9
-rw-r--r--arch/sh/drivers/dma/dma-api.c292
-rw-r--r--arch/sh/drivers/dma/dma-g2.c171
-rw-r--r--arch/sh/drivers/dma/dma-isa.c106
-rw-r--r--arch/sh/drivers/dma/dma-pvr2.c109
-rw-r--r--arch/sh/drivers/dma/dma-sh.c267
-rw-r--r--arch/sh/drivers/dma/dma-sh.h52
-rw-r--r--arch/sh/drivers/dma/dma-sysfs.c133
-rw-r--r--arch/sh/drivers/pci/Kconfig41
-rw-r--r--arch/sh/drivers/pci/Makefile16
-rw-r--r--arch/sh/drivers/pci/dma-dreamcast.c71
-rw-r--r--arch/sh/drivers/pci/fixups-dreamcast.c81
-rw-r--r--arch/sh/drivers/pci/fixups-rts7751r2d.c43
-rw-r--r--arch/sh/drivers/pci/fixups-sh03.c61
-rw-r--r--arch/sh/drivers/pci/ops-bigsur.c88
-rw-r--r--arch/sh/drivers/pci/ops-dreamcast.c169
-rw-r--r--arch/sh/drivers/pci/ops-rts7751r2d.c79
-rw-r--r--arch/sh/drivers/pci/ops-sh03.c45
-rw-r--r--arch/sh/drivers/pci/ops-snapgear.c102
-rw-r--r--arch/sh/drivers/pci/pci-auto.c555
-rw-r--r--arch/sh/drivers/pci/pci-sh7751.c417
-rw-r--r--arch/sh/drivers/pci/pci-sh7751.h303
-rw-r--r--arch/sh/drivers/pci/pci-st40.c509
-rw-r--r--arch/sh/drivers/pci/pci-st40.h136
-rw-r--r--arch/sh/drivers/pci/pci.c155
27 files changed, 4072 insertions, 0 deletions
diff --git a/arch/sh/drivers/Makefile b/arch/sh/drivers/Makefile
new file mode 100644
index 00000000000..bd6726cde39
--- /dev/null
+++ b/arch/sh/drivers/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux SuperH-specific device drivers.
+#
+
+obj-$(CONFIG_PCI) += pci/
+obj-$(CONFIG_SH_DMA) += dma/
+
diff --git a/arch/sh/drivers/dma/Kconfig b/arch/sh/drivers/dma/Kconfig
new file mode 100644
index 00000000000..0f15216cd39
--- /dev/null
+++ b/arch/sh/drivers/dma/Kconfig
@@ -0,0 +1,55 @@
+menu "DMA support"
+
+config SH_DMA
+ bool "DMA controller (DMAC) support"
+ help
+ Selecting this option will provide same API as PC's Direct Memory
+ Access Controller(8237A) for SuperH DMAC.
+
+ If unsure, say N.
+
+config NR_ONCHIP_DMA_CHANNELS
+ depends on SH_DMA
+ int "Number of on-chip DMAC channels"
+ default "4"
+ help
+ This allows you to specify the number of channels that the on-chip
+ DMAC supports. This will be 4 for SH7750/SH7751 and 8 for the
+ SH7750R/SH7751R.
+
+config NR_DMA_CHANNELS_BOOL
+ depends on SH_DMA
+ bool "Override default number of maximum DMA channels"
+ help
+ This allows you to forcibly update the maximum number of supported
+ DMA channels for a given board. If this is unset, this will default
+ to the number of channels that the on-chip DMAC has.
+
+config NR_DMA_CHANNELS
+ int "Maximum number of DMA channels"
+ depends on SH_DMA && NR_DMA_CHANNELS_BOOL
+ default NR_ONCHIP_DMA_CHANNELS
+ help
+ This allows you to specify the maximum number of DMA channels to
+ support. Setting this to a higher value allows for cascading DMACs
+ with additional channels.
+
+config DMA_PAGE_OPS
+ bool "Use DMAC for page copy/clear"
+ depends on SH_DMA && BROKEN
+ help
+ Selecting this option will use a dual-address mode configured channel
+ in the SH DMAC for copy_page()/clear_page(). Primarily a performance
+ hack.
+
+config DMA_PAGE_OPS_CHANNEL
+ depends on DMA_PAGE_OPS
+ int "DMA channel for sh memory-manager page copy/clear"
+ default "3"
+ help
+ This allows the specification of the dual address dma channel,
+ in case channel 3 is unavailable. On the SH4, channels 1,2, and 3
+ are dual-address capable.
+
+endmenu
+
diff --git a/arch/sh/drivers/dma/Makefile b/arch/sh/drivers/dma/Makefile
new file mode 100644
index 00000000000..065d4c90970
--- /dev/null
+++ b/arch/sh/drivers/dma/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the SuperH DMA specific kernel interface routines under Linux.
+#
+
+obj-y += dma-api.o dma-isa.o
+obj-$(CONFIG_SYSFS) += dma-sysfs.o
+obj-$(CONFIG_SH_DMA) += dma-sh.o
+obj-$(CONFIG_SH_DREAMCAST) += dma-pvr2.o dma-g2.o
+
diff --git a/arch/sh/drivers/dma/dma-api.c b/arch/sh/drivers/dma/dma-api.c
new file mode 100644
index 00000000000..96e3036ec2b
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-api.c
@@ -0,0 +1,292 @@
+/*
+ * arch/sh/drivers/dma/dma-api.c
+ *
+ * SuperH-specific DMA management API
+ *
+ * Copyright (C) 2003, 2004 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <asm/dma.h>
+
+DEFINE_SPINLOCK(dma_spin_lock);
+static LIST_HEAD(registered_dmac_list);
+
+/*
+ * A brief note about the reasons for this API as it stands.
+ *
+ * For starters, the old ISA DMA API didn't work for us for a number of
+ * reasons, for one, the vast majority of channels on the SH DMAC are
+ * dual-address mode only, and both the new and the old DMA APIs are after the
+ * concept of managing a DMA buffer, which doesn't overly fit this model very
+ * well. In addition to which, the new API is largely geared at IOMMUs and
+ * GARTs, and doesn't even support the channel notion very well.
+ *
+ * The other thing that's a marginal issue, is the sheer number of random DMA
+ * engines that are present (ie, in boards like the Dreamcast), some of which
+ * cascade off of the SH DMAC, and others do not. As such, there was a real
+ * need for a scalable subsystem that could deal with both single and
+ * dual-address mode usage, in addition to interoperating with cascaded DMACs.
+ *
+ * There really isn't any reason why this needs to be SH specific, though I'm
+ * not aware of too many other processors (with the exception of some MIPS)
+ * that have the same concept of a dual address mode, or any real desire to
+ * actually make use of the DMAC even if such a subsystem were exposed
+ * elsewhere.
+ *
+ * The idea for this was derived from the ARM port, which acted as an excellent
+ * reference when trying to address these issues.
+ *
+ * It should also be noted that the decision to add Yet Another DMA API(tm) to
+ * the kernel wasn't made easily, and was only decided upon after conferring
+ * with jejb with regards to the state of the old and new APIs as they applied
+ * to these circumstances. Philip Blundell was also a great help in figuring
+ * out some single-address mode DMA semantics that were otherwise rather
+ * confusing.
+ */
+
+struct dma_info *get_dma_info(unsigned int chan)
+{
+ struct list_head *pos, *tmp;
+ unsigned int total = 0;
+
+ /*
+ * Look for each DMAC's range to determine who the owner of
+ * the channel is.
+ */
+ list_for_each_safe(pos, tmp, &registered_dmac_list) {
+ struct dma_info *info = list_entry(pos, struct dma_info, list);
+
+ total += info->nr_channels;
+ if (chan > total)
+ continue;
+
+ return info;
+ }
+
+ return NULL;
+}
+
+struct dma_channel *get_dma_channel(unsigned int chan)
+{
+ struct dma_info *info = get_dma_info(chan);
+
+ if (!info)
+ return ERR_PTR(-EINVAL);
+
+ return info->channels + chan;
+}
+
+int get_dma_residue(unsigned int chan)
+{
+ struct dma_info *info = get_dma_info(chan);
+ struct dma_channel *channel = &info->channels[chan];
+
+ if (info->ops->get_residue)
+ return info->ops->get_residue(channel);
+
+ return 0;
+}
+
+int request_dma(unsigned int chan, const char *dev_id)
+{
+ struct dma_info *info = get_dma_info(chan);
+ struct dma_channel *channel = &info->channels[chan];
+
+ down(&channel->sem);
+
+ if (!info->ops || chan >= MAX_DMA_CHANNELS) {
+ up(&channel->sem);
+ return -EINVAL;
+ }
+
+ atomic_set(&channel->busy, 1);
+
+ strlcpy(channel->dev_id, dev_id, sizeof(channel->dev_id));
+
+ up(&channel->sem);
+
+ if (info->ops->request)
+ return info->ops->request(channel);
+
+ return 0;
+}
+
+void free_dma(unsigned int chan)
+{
+ struct dma_info *info = get_dma_info(chan);
+ struct dma_channel *channel = &info->channels[chan];
+
+ if (info->ops->free)
+ info->ops->free(channel);
+
+ atomic_set(&channel->busy, 0);
+}
+
+void dma_wait_for_completion(unsigned int chan)
+{
+ struct dma_info *info = get_dma_info(chan);
+ struct dma_channel *channel = &info->channels[chan];
+
+ if (channel->flags & DMA_TEI_CAPABLE) {
+ wait_event(channel->wait_queue,
+ (info->ops->get_residue(channel) == 0));
+ return;
+ }
+
+ while (info->ops->get_residue(channel))
+ cpu_relax();
+}
+
+void dma_configure_channel(unsigned int chan, unsigned long flags)
+{
+ struct dma_info *info = get_dma_info(chan);
+ struct dma_channel *channel = &info->channels[chan];
+
+ if (info->ops->configure)
+ info->ops->configure(channel, flags);
+}
+
+int dma_xfer(unsigned int chan, unsigned long from,
+ unsigned long to, size_t size, unsigned int mode)
+{
+ struct dma_info *info = get_dma_info(chan);
+ struct dma_channel *channel = &info->channels[chan];
+
+ channel->sar = from;
+ channel->dar = to;
+ channel->count = size;
+ channel->mode = mode;
+
+ return info->ops->xfer(channel);
+}
+
+#ifdef CONFIG_PROC_FS
+static int dma_read_proc(char *buf, char **start, off_t off,
+ int len, int *eof, void *data)
+{
+ struct list_head *pos, *tmp;
+ char *p = buf;
+
+ if (list_empty(&registered_dmac_list))
+ return 0;
+
+ /*
+ * Iterate over each registered DMAC
+ */
+ list_for_each_safe(pos, tmp, &registered_dmac_list) {
+ struct dma_info *info = list_entry(pos, struct dma_info, list);
+ int i;
+
+ /*
+ * Iterate over each channel
+ */
+ for (i = 0; i < info->nr_channels; i++) {
+ struct dma_channel *channel = info->channels + i;
+
+ if (!(channel->flags & DMA_CONFIGURED))
+ continue;
+
+ p += sprintf(p, "%2d: %14s %s\n", i,
+ info->name, channel->dev_id);
+ }
+ }
+
+ return p - buf;
+}
+#endif
+
+
+int __init register_dmac(struct dma_info *info)
+{
+ int i;
+
+ INIT_LIST_HEAD(&info->list);
+
+ printk(KERN_INFO "DMA: Registering %s handler (%d channel%s).\n",
+ info->name, info->nr_channels,
+ info->nr_channels > 1 ? "s" : "");
+
+ BUG_ON((info->flags & DMAC_CHANNELS_CONFIGURED) && !info->channels);
+
+ /*
+ * Don't touch pre-configured channels
+ */
+ if (!(info->flags & DMAC_CHANNELS_CONFIGURED)) {
+ unsigned int size;
+
+ size = sizeof(struct dma_channel) * info->nr_channels;
+
+ info->channels = kmalloc(size, GFP_KERNEL);
+ if (!info->channels)
+ return -ENOMEM;
+
+ memset(info->channels, 0, size);
+ }
+
+ for (i = 0; i < info->nr_channels; i++) {
+ struct dma_channel *chan = info->channels + i;
+
+ chan->chan = i;
+
+ memcpy(chan->dev_id, "Unused", 7);
+
+ if (info->flags & DMAC_CHANNELS_TEI_CAPABLE)
+ chan->flags |= DMA_TEI_CAPABLE;
+
+ init_MUTEX(&chan->sem);
+ init_waitqueue_head(&chan->wait_queue);
+
+#ifdef CONFIG_SYSFS
+ dma_create_sysfs_files(chan);
+#endif
+ }
+
+ list_add(&info->list, &registered_dmac_list);
+
+ return 0;
+}
+
+void __exit unregister_dmac(struct dma_info *info)
+{
+ if (!(info->flags & DMAC_CHANNELS_CONFIGURED))
+ kfree(info->channels);
+
+ list_del(&info->list);
+}
+
+static int __init dma_api_init(void)
+{
+ printk("DMA: Registering DMA API.\n");
+
+#ifdef CONFIG_PROC_FS
+ create_proc_read_entry("dma", 0, 0, dma_read_proc, 0);
+#endif
+
+ return 0;
+}
+
+subsys_initcall(dma_api_init);
+
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_DESCRIPTION("DMA API for SuperH");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(request_dma);
+EXPORT_SYMBOL(free_dma);
+EXPORT_SYMBOL(register_dmac);
+EXPORT_SYMBOL(get_dma_residue);
+EXPORT_SYMBOL(get_dma_info);
+EXPORT_SYMBOL(get_dma_channel);
+EXPORT_SYMBOL(dma_xfer);
+EXPORT_SYMBOL(dma_wait_for_completion);
+EXPORT_SYMBOL(dma_configure_channel);
+
diff --git a/arch/sh/drivers/dma/dma-g2.c b/arch/sh/drivers/dma/dma-g2.c
new file mode 100644
index 00000000000..231e3f6fb28
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-g2.c
@@ -0,0 +1,171 @@
+/*
+ * arch/sh/drivers/dma/dma-g2.c
+ *
+ * G2 bus DMA support
+ *
+ * Copyright (C) 2003, 2004 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/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+#include <asm/mach/sysasic.h>
+#include <asm/mach/dma.h>
+#include <asm/dma.h>
+
+struct g2_channel {
+ unsigned long g2_addr; /* G2 bus address */
+ unsigned long root_addr; /* Root bus (SH-4) address */
+ unsigned long size; /* Size (in bytes), 32-byte aligned */
+ unsigned long direction; /* Transfer direction */
+ unsigned long ctrl; /* Transfer control */
+ unsigned long chan_enable; /* Channel enable */
+ unsigned long xfer_enable; /* Transfer enable */
+ unsigned long xfer_stat; /* Transfer status */
+} __attribute__ ((aligned(32)));
+
+struct g2_status {
+ unsigned long g2_addr;
+ unsigned long root_addr;
+ unsigned long size;
+ unsigned long status;
+} __attribute__ ((aligned(16)));
+
+struct g2_dma_info {
+ struct g2_channel channel[G2_NR_DMA_CHANNELS];
+ unsigned long pad1[G2_NR_DMA_CHANNELS];
+ unsigned long wait_state;
+ unsigned long pad2[10];
+ unsigned long magic;
+ struct g2_status status[G2_NR_DMA_CHANNELS];
+} __attribute__ ((aligned(256)));
+
+static volatile struct g2_dma_info *g2_dma = (volatile struct g2_dma_info *)0xa05f7800;
+
+static irqreturn_t g2_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /* FIXME: Do some meaningful completion work here.. */
+ return IRQ_HANDLED;
+}
+
+static struct irqaction g2_dma_irq = {
+ .name = "g2 DMA handler",
+ .handler = g2_dma_interrupt,
+ .flags = SA_INTERRUPT,
+};
+
+static int g2_enable_dma(struct dma_channel *chan)
+{
+ unsigned int chan_nr = chan->chan;
+
+ g2_dma->channel[chan_nr].chan_enable = 1;
+ g2_dma->channel[chan_nr].xfer_enable = 1;
+
+ return 0;
+}
+
+static int g2_disable_dma(struct dma_channel *chan)
+{
+ unsigned int chan_nr = chan->chan;
+
+ g2_dma->channel[chan_nr].chan_enable = 0;
+ g2_dma->channel[chan_nr].xfer_enable = 0;
+
+ return 0;
+}
+
+static int g2_xfer_dma(struct dma_channel *chan)
+{
+ unsigned int chan_nr = chan->chan;
+
+ if (chan->sar & 31) {
+ printk("g2dma: unaligned source 0x%lx\n", chan->sar);
+ return -EINVAL;
+ }
+
+ if (chan->dar & 31) {
+ printk("g2dma: unaligned dest 0x%lx\n", chan->dar);
+ return -EINVAL;
+ }
+
+ /* Align the count */
+ if (chan->count & 31)
+ chan->count = (chan->count + (32 - 1)) & ~(32 - 1);
+
+ /* Fixup destination */
+ chan->dar += 0xa0800000;
+
+ /* Fixup direction */
+ chan->mode = !chan->mode;
+
+ flush_icache_range((unsigned long)chan->sar, chan->count);
+
+ g2_disable_dma(chan);
+
+ g2_dma->channel[chan_nr].g2_addr = chan->dar & 0x1fffffe0;
+ g2_dma->channel[chan_nr].root_addr = chan->sar & 0x1fffffe0;
+ g2_dma->channel[chan_nr].size = (chan->count & ~31) | 0x80000000;
+ g2_dma->channel[chan_nr].direction = chan->mode;
+
+ /*
+ * bit 0 - ???
+ * bit 1 - if set, generate a hardware event on transfer completion
+ * bit 2 - ??? something to do with suspend?
+ */
+ g2_dma->channel[chan_nr].ctrl = 5; /* ?? */
+
+ g2_enable_dma(chan);
+
+ /* debug cruft */
+ pr_debug("count, sar, dar, mode, ctrl, chan, xfer: %ld, 0x%08lx, "
+ "0x%08lx, %ld, %ld, %ld, %ld\n",
+ g2_dma->channel[chan_nr].size,
+ g2_dma->channel[chan_nr].root_addr,
+ g2_dma->channel[chan_nr].g2_addr,
+ g2_dma->channel[chan_nr].direction,
+ g2_dma->channel[chan_nr].ctrl,
+ g2_dma->channel[chan_nr].chan_enable,
+ g2_dma->channel[chan_nr].xfer_enable);
+
+ return 0;
+}
+
+static struct dma_ops g2_dma_ops = {
+ .xfer = g2_xfer_dma,
+};
+
+static struct dma_info g2_dma_info = {
+ .name = "G2 DMA",
+ .nr_channels = 4,
+ .ops = &g2_dma_ops,
+ .flags = DMAC_CHANNELS_TEI_CAPABLE,
+};
+
+static int __init g2_dma_init(void)
+{
+ setup_irq(HW_EVENT_G2_DMA, &g2_dma_irq);
+
+ /* Magic */
+ g2_dma->wait_state = 27;
+ g2_dma->magic = 0x4659404f;
+
+ return register_dmac(&g2_dma_info);
+}
+
+static void __exit g2_dma_exit(void)
+{
+ free_irq(HW_EVENT_G2_DMA, 0);
+}
+
+subsys_initcall(g2_dma_init);
+module_exit(g2_dma_exit);
+
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_DESCRIPTION("G2 bus DMA driver");
+MODULE_LICENSE("GPL");
+
diff --git a/arch/sh/drivers/dma/dma-isa.c b/arch/sh/drivers/dma/dma-isa.c
new file mode 100644
index 00000000000..1c9bc45b8bc
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-isa.c
@@ -0,0 +1,106 @@
+/*
+ * arch/sh/drivers/dma/dma-isa.c
+ *
+ * Generic ISA DMA wrapper for SH DMA API
+ *
+ * Copyright (C) 2003, 2004 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/kernel.h>
+#include <linux/module.h>
+#include <asm/dma.h>
+
+/*
+ * This implements a small wrapper set to make code using the old ISA DMA API
+ * work with the SH DMA API. Since most of the work in the new API happens
+ * at ops->xfer() time, we simply use the various set_dma_xxx() routines to
+ * fill in per-channel info, and then hand hand this off to ops->xfer() at
+ * enable_dma() time.
+ *
+ * For channels that are doing on-demand data transfer via cascading, the
+ * channel itself will still need to be configured through the new API. As
+ * such, this code is meant for only the simplest of tasks (and shouldn't be
+ * used in any new drivers at all).
+ *
+ * It should also be noted that various functions here are labelled as
+ * being deprecated. This is due to the fact that the ops->xfer() method is
+ * the preferred way of doing things (as well as just grabbing the spinlock
+ * directly). As such, any users of this interface will be warned rather
+ * loudly.
+ */
+
+unsigned long __deprecated claim_dma_lock(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dma_spin_lock, flags);
+
+ return flags;
+}
+EXPORT_SYMBOL(claim_dma_lock);
+
+void __deprecated release_dma_lock(unsigned long flags)
+{
+ spin_unlock_irqrestore(&dma_spin_lock, flags);
+}
+EXPORT_SYMBOL(release_dma_lock);
+
+void __deprecated disable_dma(unsigned int chan)
+{
+ /* Nothing */
+}
+EXPORT_SYMBOL(disable_dma);
+
+void __deprecated enable_dma(unsigned int chan)
+{
+ struct dma_info *info = get_dma_info(chan);
+ struct dma_channel *channel = &info->channels[chan];
+
+ info->ops->xfer(channel);
+}
+EXPORT_SYMBOL(enable_dma);
+
+void clear_dma_ff(unsigned int chan)
+{
+ /* Nothing */
+}
+EXPORT_SYMBOL(clear_dma_ff);
+
+void set_dma_mode(unsigned int chan, char mode)
+{
+ struct dma_info *info = get_dma_info(chan);
+ struct dma_channel *channel = &info->channels[chan];
+
+ channel->mode = mode;
+}
+EXPORT_SYMBOL(set_dma_mode);
+
+void set_dma_addr(unsigned int chan, unsigned int addr)
+{
+ struct dma_info *info = get_dma_info(chan);
+ struct dma_channel *channel = &info->channels[chan];
+
+ /*
+ * Single address mode is the only thing supported through
+ * this interface.
+ */
+ if ((channel->mode & DMA_MODE_MASK) == DMA_MODE_READ) {
+ channel->sar = addr;
+ } else {
+ channel->dar = addr;
+ }
+}
+EXPORT_SYMBOL(set_dma_addr);
+
+void set_dma_count(unsigned int chan, unsigned int count)
+{
+ struct dma_info *info = get_dma_info(chan);
+ struct dma_channel *channel = &info->channels[chan];
+
+ channel->count = count;
+}
+EXPORT_SYMBOL(set_dma_count);
+
diff --git a/arch/sh/drivers/dma/dma-pvr2.c b/arch/sh/drivers/dma/dma-pvr2.c
new file mode 100644
index 00000000000..2e1d58f2d1b
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-pvr2.c
@@ -0,0 +1,109 @@
+/*
+ * arch/sh/boards/dreamcast/dma-pvr2.c
+ *
+ * NEC PowerVR 2 (Dreamcast) DMA support
+ *
+ * Copyright (C) 2003, 2004 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/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <asm/mach/sysasic.h>
+#include <asm/mach/dma.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+
+static unsigned int xfer_complete = 0;
+static int count = 0;
+
+static irqreturn_t pvr2_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ if (get_dma_residue(PVR2_CASCADE_CHAN)) {
+ printk(KERN_WARNING "DMA: SH DMAC did not complete transfer "
+ "on channel %d, waiting..\n", PVR2_CASCADE_CHAN);
+ dma_wait_for_completion(PVR2_CASCADE_CHAN);
+ }
+
+ if (count++ < 10)
+ pr_debug("Got a pvr2 dma interrupt for channel %d\n",
+ irq - HW_EVENT_PVR2_DMA);
+
+ xfer_complete = 1;
+
+ return IRQ_HANDLED;
+}
+
+static int pvr2_request_dma(struct dma_channel *chan)
+{
+ if (ctrl_inl(PVR2_DMA_MODE) != 0)
+ return -EBUSY;
+
+ ctrl_outl(0, PVR2_DMA_LMMODE0);
+
+ return 0;
+}
+
+static int pvr2_get_dma_residue(struct dma_channel *chan)
+{
+ return xfer_complete == 0;
+}
+
+static int pvr2_xfer_dma(struct dma_channel *chan)
+{
+ if (chan->sar || !chan->dar)
+ return -EINVAL;
+
+ xfer_complete = 0;
+
+ ctrl_outl(chan->dar, PVR2_DMA_ADDR);
+ ctrl_outl(chan->count, PVR2_DMA_COUNT);
+ ctrl_outl(chan->mode & DMA_MODE_MASK, PVR2_DMA_MODE);
+
+ return 0;
+}
+
+static struct irqaction pvr2_dma_irq = {
+ .name = "pvr2 DMA handler",
+ .handler = pvr2_dma_interrupt,
+ .flags = SA_INTERRUPT,
+};
+
+static struct dma_ops pvr2_dma_ops = {
+ .request = pvr2_request_dma,
+ .get_residue = pvr2_get_dma_residue,
+ .xfer = pvr2_xfer_dma,
+};
+
+static struct dma_info pvr2_dma_info = {
+ .name = "PowerVR 2 DMA",
+ .nr_channels = 1,
+ .ops = &pvr2_dma_ops,
+ .flags = DMAC_CHANNELS_TEI_CAPABLE,
+};
+
+static int __init pvr2_dma_init(void)
+{
+ setup_irq(HW_EVENT_PVR2_DMA, &pvr2_dma_irq);
+ request_dma(PVR2_CASCADE_CHAN, "pvr2 cascade");
+
+ return register_dmac(&pvr2_dma_info);
+}
+
+static void __exit pvr2_dma_exit(void)
+{
+ free_dma(PVR2_CASCADE_CHAN);
+ free_irq(HW_EVENT_PVR2_DMA, 0);
+}
+
+subsys_initcall(pvr2_dma_init);
+module_exit(pvr2_dma_exit);
+
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_DESCRIPTION("NEC PowerVR 2 DMA driver");
+MODULE_LICENSE("GPL");
+
diff --git a/arch/sh/drivers/dma/dma-sh.c b/arch/sh/drivers/dma/dma-sh.c
new file mode 100644
index 00000000000..31dacd4444b
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-sh.c
@@ -0,0 +1,267 @@
+/*
+ * arch/sh/drivers/dma/dma-sh.c
+ *
+ * SuperH On-chip DMAC Support
+ *
+ * Copyright (C) 2000 Takashi YOSHII
+ * Copyright (C) 2003, 2004 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/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <asm/signal.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include "dma-sh.h"
+
+/*
+ * The SuperH DMAC supports a number of transmit sizes, we list them here,
+ * with their respective values as they appear in the CHCR registers.
+ *
+ * Defaults to a 64-bit transfer size.
+ */
+enum {
+ XMIT_SZ_64BIT,
+ XMIT_SZ_8BIT,
+ XMIT_SZ_16BIT,
+ XMIT_SZ_32BIT,
+ XMIT_SZ_256BIT,
+};
+
+/*
+ * The DMA count is defined as the number of bytes to transfer.
+ */
+static unsigned int ts_shift[] = {
+ [XMIT_SZ_64BIT] = 3,
+ [XMIT_SZ_8BIT] = 0,
+ [XMIT_SZ_16BIT] = 1,
+ [XMIT_SZ_32BIT] = 2,
+ [XMIT_SZ_256BIT] = 5,
+};
+
+static inline unsigned int get_dmte_irq(unsigned int chan)
+{
+ unsigned int irq;
+
+ /*
+ * Normally we could just do DMTE0_IRQ + chan outright, though in the
+ * case of the 7751R, the DMTE IRQs for channels > 4 start right above
+ * the SCIF
+ */
+
+ if (chan < 4) {
+ irq = DMTE0_IRQ + chan;
+ } else {
+ irq = DMTE4_IRQ + chan - 4;
+ }
+
+ return irq;
+}
+
+/*
+ * We determine the correct shift size based off of the CHCR transmit size
+ * for the given channel. Since we know that it will take:
+ *
+ * info->count >> ts_shift[transmit_size]
+ *
+ * iterations to complete the transfer.
+ */
+static inline unsigned int calc_xmit_shift(struct dma_channel *chan)
+{
+ u32 chcr = ctrl_inl(CHCR[chan->chan]);
+
+ chcr >>= 4;
+
+ return ts_shift[chcr & 0x0007];
+}
+
+/*
+ * The transfer end interrupt must read the chcr register to end the
+ * hardware interrupt active condition.
+ * Besides that it needs to waken any waiting process, which should handle
+ * setting up the next transfer.
+ */
+static irqreturn_t dma_tei(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct dma_channel *chan = (struct dma_channel *)dev_id;
+ u32 chcr;
+
+ chcr = ctrl_inl(CHCR[chan->chan]);
+
+ if (!(chcr & CHCR_TE))
+ return IRQ_NONE;
+
+ chcr &= ~(CHCR_IE | CHCR_DE);
+ ctrl_outl(chcr, CHCR[chan->chan]);
+
+ wake_up(&chan->wait_queue);
+
+ return IRQ_HANDLED;
+}
+
+static int sh_dmac_request_dma(struct dma_channel *chan)
+{
+ return request_irq(get_dmte_irq(chan->chan), dma_tei,
+ SA_INTERRUPT, "DMAC Transfer End", chan);
+}
+
+static void sh_dmac_free_dma(struct dma_channel *chan)
+{
+ free_irq(get_dmte_irq(chan->chan), chan);
+}
+
+static void sh_dmac_configure_channel(struct dma_channel *chan, unsigned long chcr)
+{
+ if (!chcr)
+ chcr = RS_DUAL;
+
+ ctrl_outl(chcr, CHCR[chan->chan]);
+
+ chan->flags |= DMA_CONFIGURED;
+}
+
+static void sh_dmac_enable_dma(struct dma_channel *chan)
+{
+ int irq = get_dmte_irq(chan->chan);
+ u32 chcr;
+
+ chcr = ctrl_inl(CHCR[chan->chan]);
+ chcr |= CHCR_DE | CHCR_IE;
+ ctrl_outl(chcr, CHCR[chan->chan]);
+
+ enable_irq(irq);
+}
+
+static void sh_dmac_disable_dma(struct dma_channel *chan)
+{
+ int irq = get_dmte_irq(chan->chan);
+ u32 chcr;
+
+ disable_irq(irq);
+
+ chcr = ctrl_inl(CHCR[chan->chan]);
+ chcr &= ~(CHCR_DE | CHCR_TE | CHCR_IE);
+ ctrl_outl(chcr, CHCR[chan->chan]);
+}
+
+static int sh_dmac_xfer_dma(struct dma_channel *chan)
+{
+ /*
+ * If we haven't pre-configured the channel with special flags, use
+ * the defaults.
+ */
+ if (!(chan->flags & DMA_CONFIGURED))
+ sh_dmac_configure_channel(chan, 0);
+
+ sh_dmac_disable_dma(chan);
+
+ /*
+ * Single-address mode usage note!
+ *
+ * It's important that we don't accidentally write any value to SAR/DAR
+ * (this includes 0) that hasn't been directly specified by the user if
+ * we're in single-address mode.
+ *
+ * In this case, only one address can be defined, anything else will
+ * result in a DMA address error interrupt (at least on the SH-4),
+ * which will subsequently halt the transfer.
+ *
+ * Channel 2 on the Dreamcast is a special case, as this is used for
+ * cascading to the PVR2 DMAC. In this case, we still need to write
+ * SAR and DAR, regardless of value, in order for cascading to work.
+ */
+ if (chan->sar || (mach_is_dreamcast() && chan->chan == 2))
+ ctrl_outl(chan->sar, SAR[chan->chan]);
+ if (chan->dar || (mach_is_dreamcast() && chan->chan == 2))
+ ctrl_outl(chan->dar, DAR[chan->chan]);
+
+ ctrl_outl(chan->count >> calc_xmit_shift(chan), DMATCR[chan->chan]);
+
+ sh_dmac_enable_dma(chan);
+
+ return 0;
+}
+
+static int sh_dmac_get_dma_residue(struct dma_channel *chan)
+{
+ if (!(ctrl_inl(CHCR[chan->chan]) & CHCR_DE))
+ return 0;
+
+ return ctrl_inl(DMATCR[chan->chan]) << calc_xmit_shift(chan);
+}
+
+#if defined(CONFIG_CPU_SH4)
+static irqreturn_t dma_err(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long dmaor = ctrl_inl(DMAOR);
+
+ printk("DMAE: DMAOR=%lx\n", dmaor);
+
+ ctrl_outl(ctrl_inl(DMAOR)&~DMAOR_NMIF, DMAOR);
+ ctrl_outl(ctrl_inl(DMAOR)&~DMAOR_AE, DMAOR);
+ ctrl_outl(ctrl_inl(DMAOR)|DMAOR_DME, DMAOR);
+
+ disable_irq(irq);
+
+ return IRQ_HANDLED;
+}
+#endif
+
+static struct dma_ops sh_dmac_ops = {
+ .request = sh_dmac_request_dma,
+ .free = sh_dmac_free_dma,
+ .get_residue = sh_dmac_get_dma_residue,
+ .xfer = sh_dmac_xfer_dma,
+ .configure = sh_dmac_configure_channel,
+};
+
+static struct dma_info sh_dmac_info = {
+ .name = "SuperH DMAC",
+ .nr_channels = 4,
+ .ops = &sh_dmac_ops,
+ .flags = DMAC_CHANNELS_TEI_CAPABLE,
+};
+
+static int __init sh_dmac_init(void)
+{
+ struct dma_info *info = &sh_dmac_info;
+ int i;
+
+#ifdef CONFIG_CPU_SH4
+ make_ipr_irq(DMAE_IRQ, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY);
+ i = request_irq(DMAE_IRQ, dma_err, SA_INTERRUPT, "DMAC Address Error", 0);
+ if (i < 0)
+ return i;
+#endif
+
+ for (i = 0; i < info->nr_channels; i++) {
+ int irq = get_dmte_irq(i);
+
+ make_ipr_irq(irq, DMA_IPR_ADDR, DMA_IPR_POS, DMA_PRIORITY);
+ }
+
+ ctrl_outl(0x8000 | DMAOR_DME, DMAOR);
+
+ return register_dmac(info);
+}
+
+static void __exit sh_dmac_exit(void)
+{
+#ifdef CONFIG_CPU_SH4
+ free_irq(DMAE_IRQ, 0);
+#endif
+}
+
+subsys_initcall(sh_dmac_init);
+module_exit(sh_dmac_exit);
+
+MODULE_LICENSE("GPL");
+
diff --git a/arch/sh/drivers/dma/dma-sh.h b/arch/sh/drivers/dma/dma-sh.h
new file mode 100644
index 00000000000..dd9d547539a
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-sh.h
@@ -0,0 +1,52 @@
+/*
+ * arch/sh/drivers/dma/dma-sh.h
+ *
+ * Copyright (C) 2000 Takashi YOSHII
+ * Copyright (C) 2003 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.
+ */
+#ifndef __DMA_SH_H
+#define __DMA_SH_H
+
+/* Definitions for the SuperH DMAC */
+#define REQ_L 0x00000000
+#define REQ_E 0x00080000
+#define RACK_H 0x00000000
+#define RACK_L 0x00040000
+#define ACK_R 0x00000000
+#define ACK_W 0x00020000
+#define ACK_H 0x00000000
+#define ACK_L 0x00010000
+#define DM_INC 0x00004000
+#define DM_DEC 0x00008000
+#define SM_INC 0x00001000
+#define SM_DEC 0x00002000
+#define RS_IN 0x00000200
+#define RS_OUT 0x00000300
+#define TM_BURST 0x0000080
+#define TS_8 0x00000010
+#define TS_16 0x00000020
+#define TS_32 0x00000030
+#define TS_64 0x00000000
+#define TS_BLK 0x00000040
+#define CHCR_DE 0x00000001
+#define CHCR_TE 0x00000002
+#define CHCR_IE 0x00000004
+
+/* Define the default configuration for dual address memory-memory transfer.
+ * The 0x400 value represents auto-request, external->external.
+ */
+#define RS_DUAL (DM_INC | SM_INC | 0x400 | TS_32)
+
+#define DMAOR_COD 0x00000008
+#define DMAOR_AE 0x00000004
+#define DMAOR_NMIF 0x00000002
+#define DMAOR_DME 0x00000001
+
+#define MAX_DMAC_CHANNELS (CONFIG_NR_ONCHIP_DMA_CHANNELS)
+
+#endif /* __DMA_SH_H */
+
diff --git a/arch/sh/drivers/dma/dma-sysfs.c b/arch/sh/drivers/dma/dma-sysfs.c
new file mode 100644
index 00000000000..71a6d4e7809
--- /dev/null
+++ b/arch/sh/drivers/dma/dma-sysfs.c
@@ -0,0 +1,133 @@
+/*
+ * arch/sh/drivers/dma/dma-sysfs.c
+ *
+ * sysfs interface for SH DMA API
+ *
+ * Copyright (C) 2004 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/kernel.h>
+#include <linux/init.h>
+#include <linux/sysdev.h>
+#include <linux/module.h>
+#include <asm/dma.h>
+
+static struct sysdev_class dma_sysclass = {
+ set_kset_name("dma"),
+};
+
+EXPORT_SYMBOL(dma_sysclass);
+
+static ssize_t dma_show_devices(struct sys_device *dev, char *buf)
+{
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < MAX_DMA_CHANNELS; i++) {
+ struct dma_info *info = get_dma_info(i);
+ struct dma_channel *channel = &info->channels[i];
+
+ len += sprintf(buf + len, "%2d: %14s %s\n",
+ channel->chan, info->name,
+ channel->dev_id);
+ }
+
+ return len;
+}
+
+static SYSDEV_ATTR(devices, S_IRUGO, dma_show_devices, NULL);
+
+static int __init dma_sysclass_init(void)
+{
+ int ret;
+
+ ret = sysdev_class_register(&dma_sysclass);
+ if (ret == 0)
+ sysfs_create_file(&dma_sysclass.kset.kobj, &attr_devices.attr);
+
+ return ret;
+}
+
+postcore_initcall(dma_sysclass_init);
+
+static ssize_t dma_show_dev_id(struct sys_device *dev, char *buf)
+{
+ struct dma_channel *channel = to_dma_channel(dev);
+ return sprintf(buf, "%s\n", channel->dev_id);
+}
+
+static ssize_t dma_store_dev_id(struct sys_device *dev,
+ const char *buf, size_t count)
+{
+ struct dma_channel *channel = to_dma_channel(dev);
+ strcpy(channel->dev_id, buf);
+ return count;
+}
+
+static SYSDEV_ATTR(dev_id, S_IRUGO | S_IWUSR, dma_show_dev_id, dma_store_dev_id);
+
+static ssize_t dma_store_config(struct sys_device *dev,
+ const char *buf, size_t count)
+{
+ struct dma_channel *channel = to_dma_channel(dev);
+ unsigned long config;
+
+ config = simple_strtoul(buf, NULL, 0);
+ dma_configure_channel(channel->chan, config);
+
+ return count;
+}
+
+static SYSDEV_ATTR(config, S_IWUSR, NULL, dma_store_config);
+
+static ssize_t dma_show_mode(struct sys_device *dev, char *buf)
+{
+ struct dma_channel *channel = to_dma_channel(dev);
+ return sprintf(buf, "0x%08x\n", channel->mode);
+}
+
+static ssize_t dma_store_mode(struct sys_device *dev,
+ const char *buf, size_t count)
+{
+ struct dma_channel *channel = to_dma_channel(dev);
+ channel->mode = simple_strtoul(buf, NULL, 0);
+ return count;
+}
+
+static SYSDEV_ATTR(mode, S_IRUGO | S_IWUSR, dma_show_mode, dma_store_mode);
+
+#define dma_ro_attr(field, fmt) \
+static ssize_t dma_show_##field(struct sys_device *dev, char *buf) \
+{ \
+ struct dma_channel *channel = to_dma_channel(dev); \
+ return sprintf(buf, fmt, channel->field); \
+} \
+static SYSDEV_ATTR(field, S_IRUGO, dma_show_##field, NULL);
+
+dma_ro_attr(count, "0x%08x\n");
+dma_ro_attr(flags, "0x%08lx\n");
+
+int __init dma_create_sysfs_files(struct dma_channel *chan)
+{
+ struct sys_device *dev = &chan->dev;
+ int ret;
+
+ dev->id = chan->chan;
+ dev->cls = &dma_sysclass;
+
+ ret = sysdev_register(dev);
+ if (ret)
+ return ret;
+
+ sysdev_create_file(dev, &attr_dev_id);
+ sysdev_create_file(dev, &attr_count);
+ sysdev_create_file(dev, &attr_mode);
+ sysdev_create_file(dev, &attr_flags);
+ sysdev_create_file(dev, &attr_config);
+
+ return 0;
+}
+
diff --git a/arch/sh/drivers/pci/Kconfig b/arch/sh/drivers/pci/Kconfig
new file mode 100644
index 00000000000..6d1cbbe6745
--- /dev/null
+++ b/arch/sh/drivers/pci/Kconfig
@@ -0,0 +1,41 @@
+config PCI
+ bool "PCI support"
+ help
+ Find out whether you have a PCI motherboard. PCI is the name of a
+ bus system, i.e. the way the CPU talks to the other stuff inside
+ your box. If you have PCI, say Y, otherwise N.
+
+ The PCI-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>, contains valuable
+ information about which PCI hardware does work under Linux and which
+ doesn't.
+
+config SH_PCIDMA_NONCOHERENT
+ bool "Cache and PCI noncoherent"
+ depends on PCI
+ default y
+ help
+ Enable this option if your platform does not have a CPU cache which
+ remains coherent with PCI DMA. It is safest to say 'Y', although you
+ will see better performance if you can say 'N', because the PCI DMA
+ code will not have to flush the CPU's caches. If you have a PCI host
+ bridge integrated with your SH CPU, refer carefully to the chip specs
+ to see if you can say 'N' here. Otherwise, leave it as 'Y'.
+
+# This is also board-specific
+config PCI_AUTO
+ bool
+ depends on PCI
+ default y
+
+config PCI_AUTO_UPDATE_RESOURCES
+ bool
+ depends on PCI_AUTO
+ default y if !SH_DREAMCAST
+ help
+ Selecting this option will cause the PCI auto code to leave your
+ BAR values alone. Otherwise they will be updated automatically. If
+ for some reason, you have a board that simply refuses to work
+ with its resources updated beyond what they are when the device
+ is powered up, set this to N. Everyone else will want this as Y.
+
diff --git a/arch/sh/drivers/pci/Makefile b/arch/sh/drivers/pci/Makefile
new file mode 100644
index 00000000000..365bc16a4a8
--- /dev/null
+++ b/arch/sh/drivers/pci/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the PCI specific kernel interface routines under Linux.
+#
+
+obj-y += pci.o
+obj-$(CONFIG_PCI_AUTO) += pci-auto.o
+
+obj-$(CONFIG_CPU_SUBTYPE_ST40STB1) += pci-st40.o
+obj-$(CONFIG_CPU_SUBTYPE_SH7751) += pci-sh7751.o
+
+obj-$(CONFIG_SH_DREAMCAST) += ops-dreamcast.o fixups-dreamcast.o \
+ dma-dreamcast.o
+obj-$(CONFIG_SH_SECUREEDGE5410) += ops-snapgear.o
+obj-$(CONFIG_SH_BIGSUR) += ops-bigsur.o
+obj-$(CONFIG_SH_RTS7751R2D) += ops-rts7751r2d.o fixups-rts7751r2d.o
+obj-$(CONFIG_SH_SH03) += ops-sh03.o fixups-sh03.o
diff --git a/arch/sh/drivers/pci/dma-dreamcast.c b/arch/sh/drivers/pci/dma-dreamcast.c
new file mode 100644
index 00000000000..83de7ef4e7d
--- /dev/null
+++ b/arch/sh/drivers/pci/dma-dreamcast.c
@@ -0,0 +1,71 @@
+/*
+ * arch/sh/pci/dma-dreamcast.c
+ *
+ * PCI DMA support for the Sega Dreamcast
+ *
+ * Copyright (C) 2001, 2002 M. R. Brown
+ * Copyright (C) 2002, 2003 Paul Mundt
+ *
+ * This file originally bore the message (with enclosed-$):
+ * Id: pci.c,v 1.3 2003/05/04 19:29:46 lethal Exp
+ * Dreamcast PCI: Supports SEGA Broadband Adaptor only.
+ *
+ * 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/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/device.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach/pci.h>
+
+static int gapspci_dma_used = 0;
+
+void *dreamcast_consistent_alloc(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, int flag)
+{
+ unsigned long buf;
+
+ if (dev && dev->bus != &pci_bus_type)
+ return NULL;
+
+ if (gapspci_dma_used + size > GAPSPCI_DMA_SIZE)
+ return ERR_PTR(-EINVAL);
+
+ buf = GAPSPCI_DMA_BASE + gapspci_dma_used;
+
+ gapspci_dma_used = PAGE_ALIGN(gapspci_dma_used+size);
+
+ *dma_handle = (dma_addr_t)buf;
+
+ buf = P2SEGADDR(buf);
+
+ /* Flush the dcache before we hand off the buffer */
+ dma_cache_wback_inv((void *)buf, size);
+
+ return (void *)buf;
+}
+
+int dreamcast_consistent_free(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle)
+{
+ if (dev && dev->bus != &pci_bus_type)
+ return -EINVAL;
+
+ /* XXX */
+ gapspci_dma_used = 0;
+
+ return 0;
+}
+
diff --git a/arch/sh/drivers/pci/fixups-dreamcast.c b/arch/sh/drivers/pci/fixups-dreamcast.c
new file mode 100644
index 00000000000..cf30e2fa51b
--- /dev/null
+++ b/arch/sh/drivers/pci/fixups-dreamcast.c
@@ -0,0 +1,81 @@
+/*
+ * arch/sh/pci/fixups-dreamcast.c
+ *
+ * PCI fixups for the Sega Dreamcast
+ *
+ * Copyright (C) 2001, 2002 M. R. Brown
+ * Copyright (C) 2002, 2003 Paul Mundt
+ *
+ * This file originally bore the message (with enclosed-$):
+ * Id: pci.c,v 1.3 2003/05/04 19:29:46 lethal Exp
+ * Dreamcast PCI: Supports SEGA Broadband Adaptor only.
+ *
+ * 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/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach/pci.h>
+
+static void __init gapspci_fixup_resources(struct pci_dev *dev)
+{
+ struct pci_channel *p = board_pci_channels;
+
+ printk(KERN_NOTICE "PCI: Fixing up device %s\n", pci_name(dev));
+
+ switch (dev->device) {
+ case PCI_DEVICE_ID_SEGA_BBA:
+ /*
+ * We also assume that dev->devfn == 0
+ */
+ dev->resource[1].start = p->io_resource->start + 0x100;
+ dev->resource[1].end = dev->resource[1].start + 0x200 - 1;
+ break;
+ default:
+ printk("PCI: Failed resource fixup\n");
+ }
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, gapspci_fixup_resources);
+
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+ /*
+ * We don't have any sub bus to fix up, and this is a rather
+ * stupid place to put general device fixups. Don't do it.
+ * Use the pcibios_fixups table or suffer the consequences.
+ */
+}
+
+void __init pcibios_fixup_irqs(void)
+{
+ struct pci_dev *dev = 0;
+
+ for_each_pci_dev(dev) {
+ /*
+ * The interrupt routing semantics here are quite trivial.
+ *
+ * We basically only support one interrupt, so we only bother
+ * updating a device's interrupt line with this single shared
+ * interrupt. Keeps routing quite simple, doesn't it?
+ */
+ printk(KERN_NOTICE "PCI: Fixing up IRQ routing for device %s\n",
+ pci_name(dev));
+
+ dev->irq = GAPSPCI_IRQ;
+
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+ }
+}
+
diff --git a/arch/sh/drivers/pci/fixups-rts7751r2d.c b/arch/sh/drivers/pci/fixups-rts7751r2d.c
new file mode 100644
index 00000000000..0c590fc7a08
--- /dev/null
+++ b/arch/sh/drivers/pci/fixups-rts7751r2d.c
@@ -0,0 +1,43 @@
+/*
+ * arch/sh/drivers/pci/fixups-rts7751r2d.c
+ *
+ * RTS7751R2D PCI fixups
+ *
+ * Copyright (C) 2003 Lineo uSolutions, Inc.
+ * Copyright (C) 2004 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 "pci-sh7751.h"
+#include <asm/io.h>
+
+#define PCIMCR_MRSET_OFF 0xBFFFFFFF
+#define PCIMCR_RFSH_OFF 0xFFFFFFFB
+
+int pci_fixup_pcic(void)
+{
+ unsigned long bcr1, mcr;
+
+ bcr1 = inl(SH7751_BCR1);
+ bcr1 |= 0x40080000; /* Enable Bit 19 BREQEN, set PCIC to slave */
+ outl(bcr1, PCI_REG(SH7751_PCIBCR1));
+
+ /* Enable all interrupts, so we known what to fix */
+ outl(0x0000c3ff, PCI_REG(SH7751_PCIINTM));
+ outl(0x0000380f, PCI_REG(SH7751_PCIAINTM));
+
+ outl(0xfb900047, PCI_REG(SH7751_PCICONF1));
+ outl(0xab000001, PCI_REG(SH7751_PCICONF4));
+
+ mcr = inl(SH7751_MCR);
+ mcr = (mcr & PCIMCR_MRSET_OFF) & PCIMCR_RFSH_OFF;
+ outl(mcr, PCI_REG(SH7751_PCIMCR));
+
+ outl(0x0c000000, PCI_REG(SH7751_PCICONF5));
+ outl(0xd0000000, PCI_REG(SH7751_PCICONF6));
+ outl(0x0c000000, PCI_REG(SH7751_PCILAR0));
+ outl(0x00000000, PCI_REG(SH7751_PCILAR1));
+ return 0;
+}
diff --git a/arch/sh/drivers/pci/fixups-sh03.c b/arch/sh/drivers/pci/fixups-sh03.c
new file mode 100644
index 00000000000..57ac26c2171
--- /dev/null
+++ b/arch/sh/drivers/pci/fixups-sh03.c
@@ -0,0 +1,61 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+
+/*
+ * IRQ functions
+ */
+
+int __init pcibios_map_platform_irq(u8 slot, u8 pin, struct pci_dev *dev)
+{
+ int irq;
+
+ if (dev->bus->number == 0) {
+ switch (slot) {
+ case 4: return 5; /* eth0 */
+ case 8: return 5; /* eth1 */
+ case 6: return 2; /* PCI bridge */
+ default:
+ printk("PCI: Bad IRQ mapping request for slot %d\n", slot);
+ return 2;
+ }
+ } else {
+ switch (pin) {
+ case 0: irq = 2; break;
+ case 1: irq = 2; break;
+ case 2: irq = 2; break;
+ case 3: irq = 2; break;
+ case 4: irq = 2; break;
+ default: irq = -1; break;
+ }
+ }
+ return irq;
+}
+
+static u8 __init sh03_no_swizzle(struct pci_dev *dev, u8 *pin)
+{
+ /* no swizzling */
+ return PCI_SLOT(dev->devfn);
+}
+
+static int sh03_pci_lookup_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ int irq = -1;
+
+ /* now lookup the actual IRQ on a platform specific basis (pci-'platform'.c) */
+ irq = pcibios_map_platform_irq(slot, pin, dev);
+ if( irq < 0 ) {
+ pr_debug("PCI: Error mapping IRQ on device %s\n", pci_name(dev));
+ return irq;
+ }
+
+ pr_debug("Setting IRQ for slot %s to %d\n", pci_name(dev), irq);
+
+ return irq;
+}
+
+void __init pcibios_fixup_irqs(void)
+{
+ pci_fixup_irqs(sh03_no_swizzle, sh03_pci_lookup_irq);
+}
diff --git a/arch/sh/drivers/pci/ops-bigsur.c b/arch/sh/drivers/pci/ops-bigsur.c
new file mode 100644
index 00000000000..9b43da67804
--- /dev/null
+++ b/arch/sh/drivers/pci/ops-bigsur.c
@@ -0,0 +1,88 @@
+/*
+ * linux/arch/sh/kernel/pci-bigsur.c
+ *
+ * By Dustin McIntire (dustin@sensoria.com) (c)2001
+ *
+ * Ported to new API by Paul Mundt <lethal@linux-sh.org>.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * PCI initialization for the Hitachi Big Sur Evaluation Board
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+#include "pci-sh7751.h"
+#include <asm/bigsur/bigsur.h>
+
+#define BIGSUR_PCI_IO 0x4000
+#define BIGSUR_PCI_MEM 0xfd000000
+
+static struct resource sh7751_io_resource = {
+ .name = "SH7751 IO",
+ .start = BIGSUR_PCI_IO,
+ .end = BIGSUR_PCI_IO + (64*1024) - 1,
+ .flags = IORESOURCE_IO,
+};
+
+static struct resource sh7751_mem_resource = {
+ .name = "SH7751 mem",
+ .start = BIGSUR_PCI_MEM,
+ .end = BIGSUR_PCI_MEM + (64*1024*1024) - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+extern struct pci_ops sh7751_pci_ops;
+
+struct pci_channel board_pci_channels[] = {
+ { &sh7751_pci_ops, &sh7751_io_resource, &sh7751_mem_resource, 0, 0xff },
+ { 0, }
+};
+
+static struct sh7751_pci_address_map sh7751_pci_map = {
+ .window0 = {
+ .base = SH7751_CS3_BASE_ADDR,
+ .size = BIGSUR_LSR0_SIZE,
+ },
+
+ .window1 = {
+ .base = SH7751_CS3_BASE_ADDR,
+ .size = BIGSUR_LSR1_SIZE,
+ },
+};
+
+/*
+ * Initialize the Big Sur PCI interface
+ * Setup hardware to be Central Funtion
+ * Copy the BSR regs to the PCI interface
+ * Setup PCI windows into local RAM
+ */
+int __init pcibios_init_platform(void)
+{
+ return sh7751_pcic_init(&sh7751_pci_map);
+}
+
+int pcibios_map_platform_irq(u8 slot, u8 pin)
+{
+ /*
+ * The Big Sur can be used in a CPCI chassis, but the SH7751 PCI
+ * interface is on the wrong end of the board so that it can also
+ * support a V320 CPI interface chip... Therefor the IRQ mapping is
+ * somewhat use dependent... I'l assume a linear map for now, i.e.
+ * INTA=slot0,pin0... INTD=slot3,pin0...
+ */
+ int irq = (slot + pin-1) % 4 + BIGSUR_SH7751_PCI_IRQ_BASE;
+
+ PCIDBG(2, "PCI: Mapping Big Sur IRQ for slot %d, pin %c to irq %d\n",
+ slot, pin-1+'A', irq);
+
+ return irq;
+}
+
diff --git a/arch/sh/drivers/pci/ops-dreamcast.c b/arch/sh/drivers/pci/ops-dreamcast.c
new file mode 100644
index 00000000000..69af80b93e3
--- /dev/null
+++ b/arch/sh/drivers/pci/ops-dreamcast.c
@@ -0,0 +1,169 @@
+/*
+ * arch/sh/pci/ops-dreamcast.c
+ *
+ * PCI operations for the Sega Dreamcast
+ *
+ * Copyright (C) 2001, 2002 M. R. Brown
+ * Copyright (C) 2002, 2003 Paul Mundt
+ *
+ * This file originally bore the message (with enclosed-$):
+ * Id: pci.c,v 1.3 2003/05/04 19:29:46 lethal Exp
+ * Dreamcast PCI: Supports SEGA Broadband Adaptor only.
+ *
+ * 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/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach/pci.h>
+
+static struct resource gapspci_io_resource = {
+ .name = "GAPSPCI IO",
+ .start = GAPSPCI_BBA_CONFIG,
+ .end = GAPSPCI_BBA_CONFIG + GAPSPCI_BBA_CONFIG_SIZE - 1,
+ .flags = IORESOURCE_IO,
+};
+
+static struct resource gapspci_mem_resource = {
+ .name = "GAPSPCI mem",
+ .start = GAPSPCI_DMA_BASE,
+ .end = GAPSPCI_DMA_BASE + GAPSPCI_DMA_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct pci_ops gapspci_pci_ops;
+
+struct pci_channel board_pci_channels[] = {
+ { &gapspci_pci_ops, &gapspci_io_resource,
+ &gapspci_mem_resource, 0, 1 },
+ { 0, }
+};
+
+/*
+ * The !gapspci_config_access case really shouldn't happen, ever, unless
+ * someone implicitly messes around with the last devfn value.. otherwise we
+ * only support a single device anyways, and if we didn't have a BBA, we
+ * wouldn't make it terribly far through the PCI setup anyways.
+ *
+ * Also, we could very easily support both Type 0 and Type 1 configurations
+ * here, but since it doesn't seem that there is any such implementation in
+ * existance, we don't bother.
+ *
+ * I suppose if someone actually gets around to ripping the chip out of
+ * the BBA and hanging some more devices off of it, then this might be
+ * something to take into consideration. However, due to the cost of the BBA,
+ * and the general lack of activity by DC hardware hackers, this doesn't seem
+ * likely to happen anytime soon.
+ */
+static int gapspci_config_access(unsigned char bus, unsigned int devfn)
+{
+ return (bus == 0) && (devfn == 0);
+}
+
+/*
+ * We can also actually read and write in b/w/l sizes! Thankfully this part
+ * was at least done right, and we don't have to do the stupid masking and
+ * shifting that we do on the 7751! Small wonders never cease to amaze.
+ */
+static int gapspci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val)
+{
+ *val = 0xffffffff;
+
+ if (!gapspci_config_access(bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ switch (size) {
+ case 1: *val = inb(GAPSPCI_BBA_CONFIG+where); break;
+ case 2: *val = inw(GAPSPCI_BBA_CONFIG+where); break;
+ case 4: *val = inl(GAPSPCI_BBA_CONFIG+where); break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int gapspci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val)
+{
+ if (!gapspci_config_access(bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ switch (size) {
+ case 1: outb(( u8)val, GAPSPCI_BBA_CONFIG+where); break;
+ case 2: outw((u16)val, GAPSPCI_BBA_CONFIG+where); break;
+ case 4: outl((u32)val, GAPSPCI_BBA_CONFIG+where); break;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops gapspci_pci_ops = {
+ .read = gapspci_read,
+ .write = gapspci_write,
+};
+
+/*
+ * gapspci init
+ */
+
+int __init gapspci_init(void)
+{
+ char idbuf[16];
+ int i;
+
+ /*
+ * FIXME: All of this wants documenting to some degree,
+ * even some basic register definitions would be nice.
+ *
+ * I haven't seen anything this ugly since.. maple.
+ */
+
+ for (i=0; i<16; i++)
+ idbuf[i] = inb(GAPSPCI_REGS+i);
+
+ if (strncmp(idbuf, "GAPSPCI_BRIDGE_2", 16))
+ return -ENODEV;
+
+ outl(0x5a14a501, GAPSPCI_REGS+0x18);
+
+ for (i=0; i<1000000; i++)
+ ;
+
+ if (inl(GAPSPCI_REGS+0x18) != 1)
+ return -EINVAL;
+
+ outl(0x01000000, GAPSPCI_REGS+0x20);
+ outl(0x01000000, GAPSPCI_REGS+0x24);
+
+ outl(GAPSPCI_DMA_BASE, GAPSPCI_REGS+0x28);
+ outl(GAPSPCI_DMA_BASE+GAPSPCI_DMA_SIZE, GAPSPCI_REGS+0x2c);
+
+ outl(1, GAPSPCI_REGS+0x14);
+ outl(1, GAPSPCI_REGS+0x34);
+
+ /* Setting Broadband Adapter */
+ outw(0xf900, GAPSPCI_BBA_CONFIG+0x06);
+ outl(0x00000000, GAPSPCI_BBA_CONFIG+0x30);
+ outb(0x00, GAPSPCI_BBA_CONFIG+0x3c);
+ outb(0xf0, GAPSPCI_BBA_CONFIG+0x0d);
+ outw(0x0006, GAPSPCI_BBA_CONFIG+0x04);
+ outl(0x00002001, GAPSPCI_BBA_CONFIG+0x10);
+ outl(0x01000000, GAPSPCI_BBA_CONFIG+0x14);
+
+ return 0;
+}
+
+/* Haven't done anything here as yet */
+char * __devinit pcibios_setup(char *str)
+{
+ return str;
+}
diff --git a/arch/sh/drivers/pci/ops-rts7751r2d.c b/arch/sh/drivers/pci/ops-rts7751r2d.c
new file mode 100644
index 00000000000..beafa11f4d0
--- /dev/null
+++ b/arch/sh/drivers/pci/ops-rts7751r2d.c
@@ -0,0 +1,79 @@
+/*
+ * linux/arch/sh/kernel/pci-rts7751r2d.c
+ *
+ * Author: Ian DaSilva (idasilva@mvista.com)
+ *
+ * Highly leveraged from pci-bigsur.c, written by Dustin McIntire.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * PCI initialization for the Renesas SH7751R RTS7751R2D board
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include "pci-sh7751.h"
+#include <asm/rts7751r2d/rts7751r2d.h>
+
+int __init pcibios_map_platform_irq(u8 slot, u8 pin)
+{
+ switch (slot) {
+ case 0: return IRQ_PCISLOT1; /* PCI Extend slot #1 */
+ case 1: return IRQ_PCISLOT2; /* PCI Extend slot #2 */
+ case 2: return IRQ_PCMCIA; /* PCI Cardbus Bridge */
+ case 3: return IRQ_PCIETH; /* Realtek Ethernet controller */
+ default:
+ printk("PCI: Bad IRQ mapping request for slot %d\n", slot);
+ return -1;
+ }
+}
+
+static struct resource sh7751_io_resource = {
+ .name = "SH7751_IO",
+ .start = 0x4000,
+ .end = 0x4000 + SH7751_PCI_IO_SIZE - 1,
+ .flags = IORESOURCE_IO
+};
+
+static struct resource sh7751_mem_resource = {
+ .name = "SH7751_mem",
+ .start = SH7751_PCI_MEMORY_BASE,
+ .end = SH7751_PCI_MEMORY_BASE + SH7751_PCI_MEM_SIZE - 1,
+ .flags = IORESOURCE_MEM
+};
+
+extern struct pci_ops sh7751_pci_ops;
+
+struct pci_channel board_pci_channels[] = {
+ { &sh7751_pci_ops, &sh7751_io_resource, &sh7751_mem_resource, 0, 0xff },
+ { NULL, NULL, NULL, 0, 0 },
+};
+EXPORT_SYMBOL(board_pci_channels);
+
+static struct sh7751_pci_address_map sh7751_pci_map = {
+ .window0 = {
+ .base = SH7751_CS3_BASE_ADDR,
+ .size = 0x04000000,
+ },
+
+ .window1 = {
+ .base = 0x00000000, /* Unused */
+ .size = 0x00000000, /* Unused */
+ },
+
+ .flags = SH7751_PCIC_NO_RESET,
+};
+
+int __init pcibios_init_platform(void)
+{
+ return sh7751_pcic_init(&sh7751_pci_map);
+}
+
diff --git a/arch/sh/drivers/pci/ops-sh03.c b/arch/sh/drivers/pci/ops-sh03.c
new file mode 100644
index 00000000000..df219973234
--- /dev/null
+++ b/arch/sh/drivers/pci/ops-sh03.c
@@ -0,0 +1,45 @@
+/*
+ * linux/arch/sh/drivers/pci/ops-sh03.c
+ *
+ * PCI initialization for the Interface CTP/PCI-SH03 board
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include "pci-sh7751.h"
+
+/*
+ * Description: This function sets up and initializes the pcic, sets
+ * up the BARS, maps the DRAM into the address space etc, etc.
+ */
+int __init pcibios_init_platform(void)
+{
+ return 1;
+}
+
+static struct resource sh7751_io_resource = {
+ .name = "SH03 IO",
+ .start = SH7751_PCI_IO_BASE,
+ .end = SH7751_PCI_IO_BASE + SH7751_PCI_IO_SIZE - 1,
+ .flags = IORESOURCE_IO
+};
+
+static struct resource sh7751_mem_resource = {
+ .name = "SH03 mem",
+ .start = SH7751_PCI_MEMORY_BASE,
+ .end = SH7751_PCI_MEMORY_BASE + SH7751_PCI_MEM_SIZE - 1,
+ .flags = IORESOURCE_MEM
+};
+
+extern struct pci_ops sh7751_pci_ops;
+
+struct pci_channel board_pci_channels[] = {
+ { &sh7751_pci_ops, &sh7751_io_resource, &sh7751_mem_resource, 0, 0xff },
+ { NULL, NULL, NULL, 0, 0 },
+};
+
diff --git a/arch/sh/drivers/pci/ops-snapgear.c b/arch/sh/drivers/pci/ops-snapgear.c
new file mode 100644
index 00000000000..6fdb9765c99
--- /dev/null
+++ b/arch/sh/drivers/pci/ops-snapgear.c
@@ -0,0 +1,102 @@
+/*
+ * arch/sh/drivers/pci/ops-snapgear.c
+ *
+ * Author: David McCullough <davidm@snapgear.com>
+ *
+ * Ported to new API by Paul Mundt <lethal@linux-sh.org>
+ *
+ * Highly leveraged from pci-bigsur.c, written by Dustin McIntire.
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * PCI initialization for the SnapGear boards
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <asm/io.h>
+#include "pci-sh7751.h"
+
+#define SNAPGEAR_PCI_IO 0x4000
+#define SNAPGEAR_PCI_MEM 0xfd000000
+
+/* PCI: default LOCAL memory window sizes (seen from PCI bus) */
+#define SNAPGEAR_LSR0_SIZE (64*(1<<20)) //64MB
+#define SNAPGEAR_LSR1_SIZE (64*(1<<20)) //64MB
+
+static struct resource sh7751_io_resource = {
+ .name = "SH7751 IO",
+ .start = SNAPGEAR_PCI_IO,
+ .end = SNAPGEAR_PCI_IO + (64*1024) - 1, /* 64KiB I/O */
+ .flags = IORESOURCE_IO,
+};
+
+static struct resource sh7751_mem_resource = {
+ .name = "SH7751 mem",
+ .start = SNAPGEAR_PCI_MEM,
+ .end = SNAPGEAR_PCI_MEM + (64*1024*1024) - 1, /* 64MiB mem */
+ .flags = IORESOURCE_MEM,
+};
+
+extern struct pci_ops sh7751_pci_ops;
+
+struct pci_channel board_pci_channels[] = {
+ { &sh7751_pci_ops, &sh7751_io_resource, &sh7751_mem_resource, 0, 0xff },
+ { 0, }
+};
+
+static struct sh7751_pci_address_map sh7751_pci_map = {
+ .window0 = {
+ .base = SH7751_CS2_BASE_ADDR,
+ .size = SNAPGEAR_LSR0_SIZE,
+ },
+
+ .window1 = {
+ .base = SH7751_CS2_BASE_ADDR,
+ .size = SNAPGEAR_LSR1_SIZE,
+ },
+
+ .flags = SH7751_PCIC_NO_RESET,
+};
+
+/*
+ * Initialize the SnapGear PCI interface
+ * Setup hardware to be Central Funtion
+ * Copy the BSR regs to the PCI interface
+ * Setup PCI windows into local RAM
+ */
+int __init pcibios_init_platform(void)
+{
+ return sh7751_pcic_init(&sh7751_pci_map);
+}
+
+int __init pcibios_map_platform_irq(u8 slot, u8 pin)
+{
+ int irq = -1;
+
+ switch (slot) {
+ case 8: /* the PCI bridge */ break;
+ case 11: irq = 8; break; /* USB */
+ case 12: irq = 11; break; /* PCMCIA */
+ case 13: irq = 5; break; /* eth0 */
+ case 14: irq = 8; break; /* eth1 */
+ case 15: irq = 11; break; /* safenet (unused) */
+ }
+
+ printk("PCI: Mapping SnapGear IRQ for slot %d, pin %c to irq %d\n",
+ slot, pin - 1 + 'A', irq);
+
+ return irq;
+}
+
+void __init pcibios_fixup(void)
+{
+ /* Nothing to fixup .. */
+}
+
diff --git a/arch/sh/drivers/pci/pci-auto.c b/arch/sh/drivers/pci/pci-auto.c
new file mode 100644
index 00000000000..4cef4d1d8c8
--- /dev/null
+++ b/arch/sh/drivers/pci/pci-auto.c
@@ -0,0 +1,555 @@
+/*
+ * PCI autoconfiguration library
+ *
+ * Author: Matt Porter <mporter@mvista.com>
+ *
+ * Copyright 2000, 2001 MontaVista Software Inc.
+ * Copyright 2001 Bradley D. LaRonde <brad@ltc.com>
+ * Copyright 2003 Paul Mundt <lethal@linux-sh.org>
+ *
+ * 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.
+ */
+
+/*
+ * Modified for MIPS by Jun Sun, jsun@mvista.com
+ *
+ * . Simplify the interface between pci_auto and the rest: a single function.
+ * . Assign resources from low address to upper address.
+ * . change most int to u32.
+ *
+ * Further modified to include it as mips generic code, ppopov@mvista.com.
+ *
+ * 2001-10-26 Bradley D. LaRonde <brad@ltc.com>
+ * - Add a top_bus argument to the "early config" functions so that
+ * they can set a fake parent bus pointer to convince the underlying
+ * pci ops to use type 1 configuration for sub busses.
+ * - Set bridge base and limit registers correctly.
+ * - Align io and memory base properly before and after bridge setup.
+ * - Don't fall through to pci_setup_bars for bridge.
+ * - Reformat the debug output to look more like lspci's output.
+ *
+ * Cloned for SuperH by M. R. Brown, mrbrown@0xd6.org
+ *
+ * 2003-08-05 Paul Mundt <lethal@linux-sh.org>
+ * - Don't update the BAR values on systems that already have valid addresses
+ * and don't want these updated for whatever reason, by way of a new config
+ * option check. However, we still read in the old BAR values so that they
+ * can still be reported through the debug output.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+
+#undef DEBUG
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+/*
+ * These functions are used early on before PCI scanning is done
+ * and all of the pci_dev and pci_bus structures have been created.
+ */
+static struct pci_dev *fake_pci_dev(struct pci_channel *hose,
+ int top_bus, int busnr, int devfn)
+{
+ static struct pci_dev dev;
+ static struct pci_bus bus;
+
+ dev.bus = &bus;
+ dev.sysdata = hose;
+ dev.devfn = devfn;
+ bus.number = busnr;
+ bus.ops = hose->pci_ops;
+
+ if(busnr != top_bus)
+ /* Fake a parent bus structure. */
+ bus.parent = &bus;
+ else
+ bus.parent = NULL;
+
+ return &dev;
+}
+
+#define EARLY_PCI_OP(rw, size, type) \
+int early_##rw##_config_##size(struct pci_channel *hose, \
+ int top_bus, int bus, int devfn, int offset, type value) \
+{ \
+ return pci_##rw##_config_##size( \
+ fake_pci_dev(hose, top_bus, bus, devfn), \
+ offset, value); \
+}
+
+EARLY_PCI_OP(read, byte, u8 *)
+EARLY_PCI_OP(read, word, u16 *)
+EARLY_PCI_OP(read, dword, u32 *)
+EARLY_PCI_OP(write, byte, u8)
+EARLY_PCI_OP(write, word, u16)
+EARLY_PCI_OP(write, dword, u32)
+
+static struct resource *io_resource_inuse;
+static struct resource *mem_resource_inuse;
+
+static u32 pciauto_lower_iospc;
+static u32 pciauto_upper_iospc;
+
+static u32 pciauto_lower_memspc;
+static u32 pciauto_upper_memspc;
+
+static void __init
+pciauto_setup_bars(struct pci_channel *hose,
+ int top_bus,
+ int current_bus,
+ int pci_devfn,
+ int bar_limit)
+{
+ u32 bar_response, bar_size, bar_value;
+ u32 bar, addr_mask, bar_nr = 0;
+ u32 * upper_limit;
+ u32 * lower_limit;
+ int found_mem64 = 0;
+
+ for (bar = PCI_BASE_ADDRESS_0; bar <= bar_limit; bar+=4) {
+#if !defined(CONFIG_SH_HS7751RVOIP) && !defined(CONFIG_SH_RTS7751R2D)
+ u32 bar_addr;
+
+ /* Read the old BAR value */
+ early_read_config_dword(hose, top_bus,
+ current_bus,
+ pci_devfn,
+ bar,
+ &bar_addr);
+#endif
+
+ /* Tickle the BAR and get the response */
+ early_write_config_dword(hose, top_bus,
+ current_bus,
+ pci_devfn,
+ bar,
+ 0xffffffff);
+
+ early_read_config_dword(hose, top_bus,
+ current_bus,
+ pci_devfn,
+ bar,
+ &bar_response);
+
+#if !defined(CONFIG_SH_HS7751RVOIP) && !defined(CONFIG_SH_RTS7751R2D)
+ /*
+ * Write the old BAR value back out, only update the BAR
+ * if we implicitly want resources to be updated, which
+ * is done by the generic code further down. -- PFM.
+ */
+ early_write_config_dword(hose, top_bus,
+ current_bus,
+ pci_devfn,
+ bar,
+ bar_addr);
+#endif
+
+ /* If BAR is not implemented go to the next BAR */
+ if (!bar_response)
+ continue;
+
+ /*
+ * Workaround for a BAR that doesn't use its upper word,
+ * like the ALi 1535D+ PCI DC-97 Controller Modem (M5457).
+ * bdl <brad@ltc.com>
+ */
+ if (!(bar_response & 0xffff0000))
+ bar_response |= 0xffff0000;
+
+retry:
+ /* Check the BAR type and set our address mask */
+ if (bar_response & PCI_BASE_ADDRESS_SPACE) {
+ addr_mask = PCI_BASE_ADDRESS_IO_MASK;
+ upper_limit = &pciauto_upper_iospc;
+ lower_limit = &pciauto_lower_iospc;
+ DBG(" I/O");
+ } else {
+ if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+ PCI_BASE_ADDRESS_MEM_TYPE_64)
+ found_mem64 = 1;
+
+ addr_mask = PCI_BASE_ADDRESS_MEM_MASK;
+ upper_limit = &pciauto_upper_memspc;
+ lower_limit = &pciauto_lower_memspc;
+ DBG(" Mem");
+ }
+
+
+ /* Calculate requested size */
+ bar_size = ~(bar_response & addr_mask) + 1;
+
+ /* Allocate a base address */
+ bar_value = ((*lower_limit - 1) & ~(bar_size - 1)) + bar_size;
+
+ if ((bar_value + bar_size) > *upper_limit) {
+ if (bar_response & PCI_BASE_ADDRESS_SPACE) {
+ if (io_resource_inuse->child) {
+ io_resource_inuse =
+ io_resource_inuse->child;
+ pciauto_lower_iospc =
+ io_resource_inuse->start;
+ pciauto_upper_iospc =
+ io_resource_inuse->end + 1;
+ goto retry;
+ }
+
+ } else {
+ if (mem_resource_inuse->child) {
+ mem_resource_inuse =
+ mem_resource_inuse->child;
+ pciauto_lower_memspc =
+ mem_resource_inuse->start;
+ pciauto_upper_memspc =
+ mem_resource_inuse->end + 1;
+ goto retry;
+ }
+ }
+ DBG(" unavailable -- skipping, value %x size %x\n",
+ bar_value, bar_size);
+ continue;
+ }
+
+#ifdef CONFIG_PCI_AUTO_UPDATE_RESOURCES
+ /* Write it out and update our limit */
+ early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
+ bar, bar_value);
+#endif
+
+ *lower_limit = bar_value + bar_size;
+
+ /*
+ * If we are a 64-bit decoder then increment to the
+ * upper 32 bits of the bar and force it to locate
+ * in the lower 4GB of memory.
+ */
+ if (found_mem64) {
+ bar += 4;
+ early_write_config_dword(hose, top_bus,
+ current_bus,
+ pci_devfn,
+ bar,
+ 0x00000000);
+ }
+
+ DBG(" at 0x%.8x [size=0x%x]\n", bar_value, bar_size);
+
+ bar_nr++;
+ }
+
+}
+
+static void __init
+pciauto_prescan_setup_bridge(struct pci_channel *hose,
+ int top_bus,
+ int current_bus,
+ int pci_devfn,
+ int sub_bus)
+{
+ /* Configure bus number registers */
+ early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+ PCI_PRIMARY_BUS, current_bus);
+ early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+ PCI_SECONDARY_BUS, sub_bus + 1);
+ early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+ PCI_SUBORDINATE_BUS, 0xff);
+
+ /* Align memory and I/O to 1MB and 4KB boundaries. */
+ pciauto_lower_memspc = (pciauto_lower_memspc + (0x100000 - 1))
+ & ~(0x100000 - 1);
+ pciauto_lower_iospc = (pciauto_lower_iospc + (0x1000 - 1))
+ & ~(0x1000 - 1);
+
+ /* Set base (lower limit) of address range behind bridge. */
+ early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+ PCI_MEMORY_BASE, pciauto_lower_memspc >> 16);
+ early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+ PCI_IO_BASE, (pciauto_lower_iospc & 0x0000f000) >> 8);
+ early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+ PCI_IO_BASE_UPPER16, pciauto_lower_iospc >> 16);
+
+ /* We don't support prefetchable memory for now, so disable */
+ early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+ PCI_PREF_MEMORY_BASE, 0);
+ early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+ PCI_PREF_MEMORY_LIMIT, 0);
+}
+
+static void __init
+pciauto_postscan_setup_bridge(struct pci_channel *hose,
+ int top_bus,
+ int current_bus,
+ int pci_devfn,
+ int sub_bus)
+{
+ u32 temp;
+
+ /*
+ * [jsun] we always bump up baselines a little, so that if there
+ * nothing behind P2P bridge, we don't wind up overlapping IO/MEM
+ * spaces.
+ */
+ pciauto_lower_memspc += 1;
+ pciauto_lower_iospc += 1;
+
+ /* Configure bus number registers */
+ early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+ PCI_SUBORDINATE_BUS, sub_bus);
+
+ /* Set upper limit of address range behind bridge. */
+ early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+ PCI_MEMORY_LIMIT, pciauto_lower_memspc >> 16);
+ early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+ PCI_IO_LIMIT, (pciauto_lower_iospc & 0x0000f000) >> 8);
+ early_write_config_word(hose, top_bus, current_bus, pci_devfn,
+ PCI_IO_LIMIT_UPPER16, pciauto_lower_iospc >> 16);
+
+ /* Align memory and I/O to 1MB and 4KB boundaries. */
+ pciauto_lower_memspc = (pciauto_lower_memspc + (0x100000 - 1))
+ & ~(0x100000 - 1);
+ pciauto_lower_iospc = (pciauto_lower_iospc + (0x1000 - 1))
+ & ~(0x1000 - 1);
+
+ /* Enable memory and I/O accesses, enable bus master */
+ early_read_config_dword(hose, top_bus, current_bus, pci_devfn,
+ PCI_COMMAND, &temp);
+ early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
+ PCI_COMMAND, temp | PCI_COMMAND_IO | PCI_COMMAND_MEMORY
+ | PCI_COMMAND_MASTER);
+}
+
+static void __init
+pciauto_prescan_setup_cardbus_bridge(struct pci_channel *hose,
+ int top_bus,
+ int current_bus,
+ int pci_devfn,
+ int sub_bus)
+{
+ /* Configure bus number registers */
+ early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+ PCI_PRIMARY_BUS, current_bus);
+ early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+ PCI_SECONDARY_BUS, sub_bus + 1);
+ early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+ PCI_SUBORDINATE_BUS, 0xff);
+
+ /* Align memory and I/O to 4KB and 4 byte boundaries. */
+ pciauto_lower_memspc = (pciauto_lower_memspc + (0x1000 - 1))
+ & ~(0x1000 - 1);
+ pciauto_lower_iospc = (pciauto_lower_iospc + (0x4 - 1))
+ & ~(0x4 - 1);
+
+ early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
+ PCI_CB_MEMORY_BASE_0, pciauto_lower_memspc);
+ early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
+ PCI_CB_IO_BASE_0, pciauto_lower_iospc);
+}
+
+static void __init
+pciauto_postscan_setup_cardbus_bridge(struct pci_channel *hose,
+ int top_bus,
+ int current_bus,
+ int pci_devfn,
+ int sub_bus)
+{
+ u32 temp;
+
+#if !defined(CONFIG_SH_HS7751RVOIP) && !defined(CONFIG_SH_RTS7751R2D)
+ /*
+ * [jsun] we always bump up baselines a little, so that if there
+ * nothing behind P2P bridge, we don't wind up overlapping IO/MEM
+ * spaces.
+ */
+ pciauto_lower_memspc += 1;
+ pciauto_lower_iospc += 1;
+#endif
+
+ /*
+ * Configure subordinate bus number. The PCI subsystem
+ * bus scan will renumber buses (reserving three additional
+ * for this PCI<->CardBus bridge for the case where a CardBus
+ * adapter contains a P2P or CB2CB bridge.
+ */
+
+ early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+ PCI_SUBORDINATE_BUS, sub_bus);
+
+ /*
+ * Reserve an additional 4MB for mem space and 16KB for
+ * I/O space. This should cover any additional space
+ * requirement of unusual CardBus devices with
+ * additional bridges that can consume more address space.
+ *
+ * Although pcmcia-cs currently will reprogram bridge
+ * windows, the goal is to add an option to leave them
+ * alone and use the bridge window ranges as the regions
+ * that are searched for free resources upon hot-insertion
+ * of a device. This will allow a PCI<->CardBus bridge
+ * configured by this routine to happily live behind a
+ * P2P bridge in a system.
+ */
+#if defined(CONFIG_SH_HS7751RVOIP) || defined(CONFIG_SH_RTS7751R2D)
+ pciauto_lower_memspc += 0x00400000;
+ pciauto_lower_iospc += 0x00004000;
+#endif
+
+ /* Align memory and I/O to 4KB and 4 byte boundaries. */
+ pciauto_lower_memspc = (pciauto_lower_memspc + (0x1000 - 1))
+ & ~(0x1000 - 1);
+ pciauto_lower_iospc = (pciauto_lower_iospc + (0x4 - 1))
+ & ~(0x4 - 1);
+ /* Set up memory and I/O filter limits, assume 32-bit I/O space */
+ early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
+ PCI_CB_MEMORY_LIMIT_0, pciauto_lower_memspc - 1);
+ early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
+ PCI_CB_IO_LIMIT_0, pciauto_lower_iospc - 1);
+
+ /* Enable memory and I/O accesses, enable bus master */
+ early_read_config_dword(hose, top_bus, current_bus, pci_devfn,
+ PCI_COMMAND, &temp);
+ early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
+ PCI_COMMAND, temp | PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER);
+}
+
+#define PCIAUTO_IDE_MODE_MASK 0x05
+
+static int __init
+pciauto_bus_scan(struct pci_channel *hose, int top_bus, int current_bus)
+{
+ int sub_bus;
+ u32 pci_devfn, pci_class, cmdstat, found_multi=0;
+ unsigned short vid, did;
+ unsigned char header_type;
+ int devfn_start = 0;
+ int devfn_stop = 0xff;
+
+ sub_bus = current_bus;
+
+ if (hose->first_devfn)
+ devfn_start = hose->first_devfn;
+ if (hose->last_devfn)
+ devfn_stop = hose->last_devfn;
+
+ for (pci_devfn=devfn_start; pci_devfn<devfn_stop; pci_devfn++) {
+
+ if (PCI_FUNC(pci_devfn) && !found_multi)
+ continue;
+
+ early_read_config_word(hose, top_bus, current_bus, pci_devfn,
+ PCI_VENDOR_ID, &vid);
+
+ if (vid == 0xffff) continue;
+
+ early_read_config_byte(hose, top_bus, current_bus, pci_devfn,
+ PCI_HEADER_TYPE, &header_type);
+
+ if (!PCI_FUNC(pci_devfn))
+ found_multi = header_type & 0x80;
+
+ early_read_config_word(hose, top_bus, current_bus, pci_devfn,
+ PCI_DEVICE_ID, &did);
+
+ early_read_config_dword(hose, top_bus, current_bus, pci_devfn,
+ PCI_CLASS_REVISION, &pci_class);
+
+ DBG("%.2x:%.2x.%x Class %.4x: %.4x:%.4x",
+ current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn),
+ pci_class >> 16, vid, did);
+ if (pci_class & 0xff)
+ DBG(" (rev %.2x)", pci_class & 0xff);
+ DBG("\n");
+
+ if ((pci_class >> 16) == PCI_CLASS_BRIDGE_PCI) {
+ DBG(" Bridge: primary=%.2x, secondary=%.2x\n",
+ current_bus, sub_bus + 1);
+#if defined(CONFIG_SH_HS7751RVOIP) || defined(CONFIG_SH_RTS7751R2D)
+ pciauto_setup_bars(hose, top_bus, current_bus, pci_devfn, PCI_BASE_ADDRESS_1);
+#endif
+ pciauto_prescan_setup_bridge(hose, top_bus, current_bus,
+ pci_devfn, sub_bus);
+ DBG("Scanning sub bus %.2x, I/O 0x%.8x, Mem 0x%.8x\n",
+ sub_bus + 1,
+ pciauto_lower_iospc, pciauto_lower_memspc);
+ sub_bus = pciauto_bus_scan(hose, top_bus, sub_bus+1);
+ DBG("Back to bus %.2x\n", current_bus);
+ pciauto_postscan_setup_bridge(hose, top_bus, current_bus,
+ pci_devfn, sub_bus);
+ continue;
+ } else if ((pci_class >> 16) == PCI_CLASS_BRIDGE_CARDBUS) {
+ DBG(" CARDBUS Bridge: primary=%.2x, secondary=%.2x\n",
+ current_bus, sub_bus + 1);
+ DBG("PCI Autoconfig: Found CardBus bridge, device %d function %d\n", PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn));
+ /* Place CardBus Socket/ExCA registers */
+ pciauto_setup_bars(hose, top_bus, current_bus, pci_devfn, PCI_BASE_ADDRESS_0);
+
+ pciauto_prescan_setup_cardbus_bridge(hose, top_bus,
+ current_bus, pci_devfn, sub_bus);
+
+ DBG("Scanning sub bus %.2x, I/O 0x%.8x, Mem 0x%.8x\n",
+ sub_bus + 1,
+ pciauto_lower_iospc, pciauto_lower_memspc);
+ sub_bus = pciauto_bus_scan(hose, top_bus, sub_bus+1);
+ DBG("Back to bus %.2x, sub_bus is %x\n", current_bus, sub_bus);
+ pciauto_postscan_setup_cardbus_bridge(hose, top_bus,
+ current_bus, pci_devfn, sub_bus);
+ continue;
+ } else if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) {
+
+ unsigned char prg_iface;
+
+ early_read_config_byte(hose, top_bus, current_bus,
+ pci_devfn, PCI_CLASS_PROG, &prg_iface);
+ if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) {
+ DBG("Skipping legacy mode IDE controller\n");
+ continue;
+ }
+ }
+
+ /*
+ * Found a peripheral, enable some standard
+ * settings
+ */
+ early_read_config_dword(hose, top_bus, current_bus, pci_devfn,
+ PCI_COMMAND, &cmdstat);
+ early_write_config_dword(hose, top_bus, current_bus, pci_devfn,
+ PCI_COMMAND, cmdstat | PCI_COMMAND_IO |
+ PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER);
+#if !defined(CONFIG_SH_HS7751RVOIP) && !defined(CONFIG_SH_RTS7751R2D)
+ early_write_config_byte(hose, top_bus, current_bus, pci_devfn,
+ PCI_LATENCY_TIMER, 0x80);
+#endif
+
+ /* Allocate PCI I/O and/or memory space */
+ pciauto_setup_bars(hose, top_bus, current_bus, pci_devfn, PCI_BASE_ADDRESS_5);
+ }
+ return sub_bus;
+}
+
+int __init
+pciauto_assign_resources(int busno, struct pci_channel *hose)
+{
+ /* setup resource limits */
+ io_resource_inuse = hose->io_resource;
+ mem_resource_inuse = hose->mem_resource;
+
+ pciauto_lower_iospc = io_resource_inuse->start;
+ pciauto_upper_iospc = io_resource_inuse->end + 1;
+ pciauto_lower_memspc = mem_resource_inuse->start;
+ pciauto_upper_memspc = mem_resource_inuse->end + 1;
+ DBG("Autoconfig PCI channel 0x%p\n", hose);
+ DBG("Scanning bus %.2x, I/O 0x%.8x:0x%.8x, Mem 0x%.8x:0x%.8x\n",
+ busno, pciauto_lower_iospc, pciauto_upper_iospc,
+ pciauto_lower_memspc, pciauto_upper_memspc);
+
+ return pciauto_bus_scan(hose, busno, busno);
+}
diff --git a/arch/sh/drivers/pci/pci-sh7751.c b/arch/sh/drivers/pci/pci-sh7751.c
new file mode 100644
index 00000000000..30b14ac7ae5
--- /dev/null
+++ b/arch/sh/drivers/pci/pci-sh7751.c
@@ -0,0 +1,417 @@
+/*
+ * Low-Level PCI Support for the SH7751
+ *
+ * Dustin McIntire (dustin@sensoria.com)
+ * Derived from arch/i386/kernel/pci-*.c which bore the message:
+ * (c) 1999--2000 Martin Mares <mj@ucw.cz>
+ *
+ * Ported to the new API by Paul Mundt <lethal@linux-sh.org>
+ * With cleanup by Paul van Gool <pvangool@mimotech.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+
+#include <asm/machvec.h>
+#include <asm/io.h>
+#include "pci-sh7751.h"
+
+static unsigned int pci_probe = PCI_PROBE_CONF1;
+extern int pci_fixup_pcic(void);
+
+void pcibios_fixup_irqs(void) __attribute__ ((weak));
+
+/*
+ * Direct access to PCI hardware...
+ */
+
+#define CONFIG_CMD(bus, devfn, where) (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
+
+/*
+ * Functions for accessing PCI configuration space with type 1 accesses
+ */
+static int sh7751_pci_read(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ unsigned long flags;
+ u32 data;
+
+ /*
+ * PCIPDR may only be accessed as 32 bit words,
+ * so we must do byte alignment by hand
+ */
+ local_irq_save(flags);
+ outl(CONFIG_CMD(bus,devfn,where), PCI_REG(SH7751_PCIPAR));
+ data = inl(PCI_REG(SH7751_PCIPDR));
+ local_irq_restore(flags);
+
+ switch (size) {
+ case 1:
+ *val = (data >> ((where & 3) << 3)) & 0xff;
+ break;
+ case 2:
+ *val = (data >> ((where & 2) << 3)) & 0xffff;
+ break;
+ case 4:
+ *val = data;
+ break;
+ default:
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+/*
+ * Since SH7751 only does 32bit access we'll have to do a read,
+ * mask,write operation.
+ * We'll allow an odd byte offset, though it should be illegal.
+ */
+static int sh7751_pci_write(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ unsigned long flags;
+ int shift;
+ u32 data;
+
+ local_irq_save(flags);
+ outl(CONFIG_CMD(bus,devfn,where), PCI_REG(SH7751_PCIPAR));
+ data = inl(PCI_REG(SH7751_PCIPDR));
+ local_irq_restore(flags);
+
+ switch (size) {
+ case 1:
+ shift = (where & 3) << 3;
+ data &= ~(0xff << shift);
+ data |= ((val & 0xff) << shift);
+ break;
+ case 2:
+ shift = (where & 2) << 3;
+ data &= ~(0xffff << shift);
+ data |= ((val & 0xffff) << shift);
+ break;
+ case 4:
+ data = val;
+ break;
+ default:
+ return PCIBIOS_FUNC_NOT_SUPPORTED;
+ }
+
+ outl(data, PCI_REG(SH7751_PCIPDR));
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+#undef CONFIG_CMD
+
+struct pci_ops sh7751_pci_ops = {
+ .read = sh7751_pci_read,
+ .write = sh7751_pci_write,
+};
+
+static int __init pci_check_direct(void)
+{
+ unsigned int tmp, id;
+
+ /* check for SH7751/SH7751R hardware */
+ id = inl(SH7751_PCIREG_BASE+SH7751_PCICONF0);
+ if (id != ((SH7751_DEVICE_ID << 16) | SH7751_VENDOR_ID) &&
+ id != ((SH7751R_DEVICE_ID << 16) | SH7751_VENDOR_ID)) {
+ pr_debug("PCI: This is not an SH7751(R) (%x)\n", id);
+ return -ENODEV;
+ }
+
+ /*
+ * Check if configuration works.
+ */
+ if (pci_probe & PCI_PROBE_CONF1) {
+ tmp = inl (PCI_REG(SH7751_PCIPAR));
+ outl (0x80000000, PCI_REG(SH7751_PCIPAR));
+ if (inl (PCI_REG(SH7751_PCIPAR)) == 0x80000000) {
+ outl (tmp, PCI_REG(SH7751_PCIPAR));
+ printk(KERN_INFO "PCI: Using configuration type 1\n");
+ request_region(PCI_REG(SH7751_PCIPAR), 8, "PCI conf1");
+ return 0;
+ }
+ outl (tmp, PCI_REG(SH7751_PCIPAR));
+ }
+
+ pr_debug("PCI: pci_check_direct failed\n");
+ return -EINVAL;
+}
+
+/***************************************************************************************/
+
+/*
+ * Handle bus scanning and fixups ....
+ */
+
+static void __init pci_fixup_ide_bases(struct pci_dev *d)
+{
+ int i;
+
+ /*
+ * PCI IDE controllers use non-standard I/O port decoding, respect it.
+ */
+ if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE)
+ return;
+ pr_debug("PCI: IDE base address fixup for %s\n", pci_name(d));
+ for(i=0; i<4; i++) {
+ struct resource *r = &d->resource[i];
+ if ((r->start & ~0x80) == 0x374) {
+ r->start |= 2;
+ r->end = r->start;
+ }
+ }
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases);
+
+/*
+ * Called after each bus is probed, but before its children
+ * are examined.
+ */
+
+void __init pcibios_fixup_bus(struct pci_bus *b)
+{
+ pci_read_bridge_bases(b);
+}
+
+/*
+ * 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.
+ *
+ * Note that the platform specific initialization (BSC registers, and memory
+ * space mapping) will be called via the machine vectors (sh_mv.mv_pci_init()) if it
+ * exitst and via the platform defined function pcibios_init_platform().
+ * See pci_bigsur.c for implementation;
+ *
+ * The BIOS version of the pci functions is not yet implemented but it is left
+ * in for completeness. Currently an error will be genereated at compile time.
+ */
+
+static int __init sh7751_pci_init(void)
+{
+ int ret;
+
+ pr_debug("PCI: Starting intialization.\n");
+ if ((ret = pci_check_direct()) != 0)
+ return ret;
+
+ return pcibios_init_platform();
+}
+
+subsys_initcall(sh7751_pci_init);
+
+static int __init __area_sdram_check(unsigned int area)
+{
+ u32 word;
+
+ word = inl(SH7751_BCR1);
+ /* check BCR for SDRAM in area */
+ if(((word >> area) & 1) == 0) {
+ printk("PCI: Area %d is not configured for SDRAM. BCR1=0x%x\n",
+ area, word);
+ return 0;
+ }
+ outl(word, PCI_REG(SH7751_PCIBCR1));
+
+ word = (u16)inw(SH7751_BCR2);
+ /* check BCR2 for 32bit SDRAM interface*/
+ if(((word >> (area << 1)) & 0x3) != 0x3) {
+ printk("PCI: Area %d is not 32 bit SDRAM. BCR2=0x%x\n",
+ area, word);
+ return 0;
+ }
+ outl(word, PCI_REG(SH7751_PCIBCR2));
+
+ return 1;
+}
+
+int __init sh7751_pcic_init(struct sh7751_pci_address_map *map)
+{
+ u32 reg;
+ u32 word;
+
+ /* Set the BCR's to enable PCI access */
+ reg = inl(SH7751_BCR1);
+ reg |= 0x80000;
+ outl(reg, SH7751_BCR1);
+
+ /* Turn the clocks back on (not done in reset)*/
+ outl(0, PCI_REG(SH7751_PCICLKR));
+ /* Clear Powerdown IRQ's (not done in reset) */
+ word = SH7751_PCIPINT_D3 | SH7751_PCIPINT_D0;
+ outl(word, PCI_REG(SH7751_PCIPINT));
+
+ /*
+ * This code is unused for some boards as it is done in the
+ * bootloader and doing it here means the MAC addresses loaded
+ * by the bootloader get lost.
+ */
+ if (!(map->flags & SH7751_PCIC_NO_RESET)) {
+ /* toggle PCI reset pin */
+ word = SH7751_PCICR_PREFIX | SH7751_PCICR_PRST;
+ outl(word,PCI_REG(SH7751_PCICR));
+ /* Wait for a long time... not 1 sec. but long enough */
+ mdelay(100);
+ word = SH7751_PCICR_PREFIX;
+ outl(word,PCI_REG(SH7751_PCICR));
+ }
+
+ /* set the command/status bits to:
+ * Wait Cycle Control + Parity Enable + Bus Master +
+ * Mem space enable
+ */
+ word = SH7751_PCICONF1_WCC | SH7751_PCICONF1_PER |
+ SH7751_PCICONF1_BUM | SH7751_PCICONF1_MES;
+ outl(word, PCI_REG(SH7751_PCICONF1));
+
+ /* define this host as the host bridge */
+ word = SH7751_PCI_HOST_BRIDGE << 24;
+ outl(word, PCI_REG(SH7751_PCICONF2));
+
+ /* Set IO and Mem windows to local address
+ * Make PCI and local address the same for easy 1 to 1 mapping
+ * Window0 = map->window0.size @ non-cached area base = SDRAM
+ * Window1 = map->window1.size @ cached area base = SDRAM
+ */
+ word = map->window0.size - 1;
+ outl(word, PCI_REG(SH7751_PCILSR0));
+ word = map->window1.size - 1;
+ outl(word, PCI_REG(SH7751_PCILSR1));
+ /* Set the values on window 0 PCI config registers */
+ word = P2SEGADDR(map->window0.base);
+ outl(word, PCI_REG(SH7751_PCILAR0));
+ outl(word, PCI_REG(SH7751_PCICONF5));
+ /* Set the values on window 1 PCI config registers */
+ word = PHYSADDR(map->window1.base);
+ outl(word, PCI_REG(SH7751_PCILAR1));
+ outl(word, PCI_REG(SH7751_PCICONF6));
+
+ /* Set the local 16MB PCI memory space window to
+ * the lowest PCI mapped address
+ */
+ word = PCIBIOS_MIN_MEM & SH7751_PCIMBR_MASK;
+ PCIDBG(2,"PCI: Setting upper bits of Memory window to 0x%x\n", word);
+ outl(word , PCI_REG(SH7751_PCIMBR));
+
+ /* Map IO space into PCI IO window
+ * The IO window is 64K-PCIBIOS_MIN_IO in size
+ * IO addresses will be translated to the
+ * PCI IO window base address
+ */
+ PCIDBG(3,"PCI: Mapping IO address 0x%x - 0x%x to base 0x%x\n", PCIBIOS_MIN_IO,
+ (64*1024), SH7751_PCI_IO_BASE+PCIBIOS_MIN_IO);
+
+ /*
+ * XXX: For now, leave this board-specific. In the event we have other
+ * boards that need to do similar work, this can be wrapped.
+ */
+#ifdef CONFIG_SH_BIGSUR
+ bigsur_port_map(PCIBIOS_MIN_IO, (64*1024), SH7751_PCI_IO_BASE+PCIBIOS_MIN_IO,0);
+#endif
+
+ /* Make sure the MSB's of IO window are set to access PCI space correctly */
+ word = PCIBIOS_MIN_IO & SH7751_PCIIOBR_MASK;
+ PCIDBG(2,"PCI: Setting upper bits of IO window to 0x%x\n", word);
+ outl(word, PCI_REG(SH7751_PCIIOBR));
+
+ /* Set PCI WCRx, BCRx's, copy from BSC locations */
+
+ /* check BCR for SDRAM in specified area */
+ switch (map->window0.base) {
+ case SH7751_CS0_BASE_ADDR: word = __area_sdram_check(0); break;
+ case SH7751_CS1_BASE_ADDR: word = __area_sdram_check(1); break;
+ case SH7751_CS2_BASE_ADDR: word = __area_sdram_check(2); break;
+ case SH7751_CS3_BASE_ADDR: word = __area_sdram_check(3); break;
+ case SH7751_CS4_BASE_ADDR: word = __area_sdram_check(4); break;
+ case SH7751_CS5_BASE_ADDR: word = __area_sdram_check(5); break;
+ case SH7751_CS6_BASE_ADDR: word = __area_sdram_check(6); break;
+ }
+
+ if (!word)
+ return 0;
+
+ /* configure the wait control registers */
+ word = inl(SH7751_WCR1);
+ outl(word, PCI_REG(SH7751_PCIWCR1));
+ word = inl(SH7751_WCR2);
+ outl(word, PCI_REG(SH7751_PCIWCR2));
+ word = inl(SH7751_WCR3);
+ outl(word, PCI_REG(SH7751_PCIWCR3));
+ word = inl(SH7751_MCR);
+ outl(word, PCI_REG(SH7751_PCIMCR));
+
+ /* NOTE: I'm ignoring the PCI error IRQs for now..
+ * TODO: add support for the internal error interrupts and
+ * DMA interrupts...
+ */
+
+#ifdef CONFIG_SH_RTS7751R2D
+ pci_fixup_pcic();
+#endif
+
+ /* SH7751 init done, set central function init complete */
+ /* use round robin mode to stop a device starving/overruning */
+ word = SH7751_PCICR_PREFIX | SH7751_PCICR_CFIN | SH7751_PCICR_ARBM;
+ outl(word,PCI_REG(SH7751_PCICR));
+
+ return 1;
+}
+
+char * __init pcibios_setup(char *str)
+{
+ if (!strcmp(str, "off")) {
+ pci_probe = 0;
+ return NULL;
+ }
+
+ return str;
+}
+
+/*
+ * IRQ functions
+ */
+static u8 __init sh7751_no_swizzle(struct pci_dev *dev, u8 *pin)
+{
+ /* no swizzling */
+ return PCI_SLOT(dev->devfn);
+}
+
+static int sh7751_pci_lookup_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+ int irq = -1;
+
+ /* now lookup the actual IRQ on a platform specific basis (pci-'platform'.c) */
+ irq = pcibios_map_platform_irq(slot,pin);
+ if( irq < 0 ) {
+ pr_debug("PCI: Error mapping IRQ on device %s\n", pci_name(dev));
+ return irq;
+ }
+
+ pr_debug("Setting IRQ for slot %s to %d\n", pci_name(dev), irq);
+
+ return irq;
+}
+
+void __init pcibios_fixup_irqs(void)
+{
+ pci_fixup_irqs(sh7751_no_swizzle, sh7751_pci_lookup_irq);
+}
+
diff --git a/arch/sh/drivers/pci/pci-sh7751.h b/arch/sh/drivers/pci/pci-sh7751.h
new file mode 100644
index 00000000000..1fee5cae10d
--- /dev/null
+++ b/arch/sh/drivers/pci/pci-sh7751.h
@@ -0,0 +1,303 @@
+/*
+ * Low-Level PCI Support for SH7751 targets
+ *
+ * Dustin McIntire (dustin@sensoria.com) (c) 2001
+ * Paul Mundt (lethal@linux-sh.org) (c) 2003
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ */
+
+#ifndef _PCI_SH7751_H_
+#define _PCI_SH7751_H_
+
+#include <linux/pci.h>
+
+/* set debug level 4=verbose...1=terse */
+//#define DEBUG_PCI 3
+#undef DEBUG_PCI
+
+#ifdef DEBUG_PCI
+#define PCIDBG(n, x...) { if(DEBUG_PCI>=n) printk(x); }
+#else
+#define PCIDBG(n, x...)
+#endif
+
+/* startup values */
+#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
+
+/* Platform Specific Values */
+#define SH7751_VENDOR_ID 0x1054
+#define SH7751_DEVICE_ID 0x3505
+#define SH7751R_DEVICE_ID 0x350e
+
+/* SH7751 Specific Values */
+#define SH7751_PCI_CONFIG_BASE 0xFD000000 /* Config space base addr */
+#define SH7751_PCI_CONFIG_SIZE 0x1000000 /* Config space size */
+#define SH7751_PCI_MEMORY_BASE 0xFD000000 /* Memory space base addr */
+#define SH7751_PCI_MEM_SIZE 0x01000000 /* Size of Memory window */
+#define SH7751_PCI_IO_BASE 0xFE240000 /* IO space base address */
+#define SH7751_PCI_IO_SIZE 0x40000 /* Size of IO window */
+
+#define SH7751_PCIREG_BASE 0xFE200000 /* PCI regs base address */
+#define PCI_REG(n) (SH7751_PCIREG_BASE+ n)
+
+#define SH7751_PCICONF0 0x0 /* PCI Config Reg 0 */
+ #define SH7751_PCICONF0_DEVID 0xFFFF0000 /* Device ID */
+ #define SH7751_PCICONF0_VNDID 0x0000FFFF /* Vendor ID */
+#define SH7751_PCICONF1 0x4 /* PCI Config Reg 1 */
+ #define SH7751_PCICONF1_DPE 0x80000000 /* Data Parity Error */
+ #define SH7751_PCICONF1_SSE 0x40000000 /* System Error Status */
+ #define SH7751_PCICONF1_RMA 0x20000000 /* Master Abort */
+ #define SH7751_PCICONF1_RTA 0x10000000 /* Target Abort Rx Status */
+ #define SH7751_PCICONF1_STA 0x08000000 /* Target Abort Exec Status */
+ #define SH7751_PCICONF1_DEV 0x06000000 /* Timing Status */
+ #define SH7751_PCICONF1_DPD 0x01000000 /* Data Parity Status */
+ #define SH7751_PCICONF1_FBBC 0x00800000 /* Back 2 Back Status */
+ #define SH7751_PCICONF1_UDF 0x00400000 /* User Defined Status */
+ #define SH7751_PCICONF1_66M 0x00200000 /* 66Mhz Operation Status */
+ #define SH7751_PCICONF1_PM 0x00100000 /* Power Management Status */
+ #define SH7751_PCICONF1_PBBE 0x00000200 /* Back 2 Back Control */
+ #define SH7751_PCICONF1_SER 0x00000100 /* SERR Output Control */
+ #define SH7751_PCICONF1_WCC 0x00000080 /* Wait Cycle Control */
+ #define SH7751_PCICONF1_PER 0x00000040 /* Parity Error Response */
+ #define SH7751_PCICONF1_VPS 0x00000020 /* VGA Pallet Snoop */
+ #define SH7751_PCICONF1_MWIE 0x00000010 /* Memory Write+Invalidate */
+ #define SH7751_PCICONF1_SPC 0x00000008 /* Special Cycle Control */
+ #define SH7751_PCICONF1_BUM 0x00000004 /* Bus Master Control */
+ #define SH7751_PCICONF1_MES 0x00000002 /* Memory Space Control */
+ #define SH7751_PCICONF1_IOS 0x00000001 /* I/O Space Control */
+#define SH7751_PCICONF2 0x8 /* PCI Config Reg 2 */
+ #define SH7751_PCICONF2_BCC 0xFF000000 /* Base Class Code */
+ #define SH7751_PCICONF2_SCC 0x00FF0000 /* Sub-Class Code */
+ #define SH7751_PCICONF2_RLPI 0x0000FF00 /* Programming Interface */
+ #define SH7751_PCICONF2_REV 0x000000FF /* Revision ID */
+#define SH7751_PCICONF3 0xC /* PCI Config Reg 3 */
+ #define SH7751_PCICONF3_BIST7 0x80000000 /* Bist Supported */
+ #define SH7751_PCICONF3_BIST6 0x40000000 /* Bist Executing */
+ #define SH7751_PCICONF3_BIST3_0 0x0F000000 /* Bist Passed */
+ #define SH7751_PCICONF3_HD7 0x00800000 /* Single Funtion device */
+ #define SH7751_PCICONF3_HD6_0 0x007F0000 /* Configuration Layout */
+ #define SH7751_PCICONF3_LAT 0x0000FF00 /* Latency Timer */
+ #define SH7751_PCICONF3_CLS 0x000000FF /* Cache Line Size */
+#define SH7751_PCICONF4 0x10 /* PCI Config Reg 4 */
+ #define SH7751_PCICONF4_BASE 0xFFFFFFFC /* I/O Space Base Addr */
+ #define SH7751_PCICONF4_ASI 0x00000001 /* Address Space Type */
+#define SH7751_PCICONF5 0x14 /* PCI Config Reg 5 */
+ #define SH7751_PCICONF5_BASE 0xFFFFFFF0 /* Mem Space Base Addr */
+ #define SH7751_PCICONF5_LAP 0x00000008 /* Prefetch Enabled */
+ #define SH7751_PCICONF5_LAT 0x00000006 /* Local Memory type */
+ #define SH7751_PCICONF5_ASI 0x00000001 /* Address Space Type */
+#define SH7751_PCICONF6 0x18 /* PCI Config Reg 6 */
+ #define SH7751_PCICONF6_BASE 0xFFFFFFF0 /* Mem Space Base Addr */
+ #define SH7751_PCICONF6_LAP 0x00000008 /* Prefetch Enabled */
+ #define SH7751_PCICONF6_LAT 0x00000006 /* Local Memory type */
+ #define SH7751_PCICONF6_ASI 0x00000001 /* Address Space Type */
+/* PCICONF7 - PCICONF10 are undefined */
+#define SH7751_PCICONF11 0x2C /* PCI Config Reg 11 */
+ #define SH7751_PCICONF11_SSID 0xFFFF0000 /* Subsystem ID */
+ #define SH7751_PCICONF11_SVID 0x0000FFFF /* Subsystem Vendor ID */
+/* PCICONF12 is undefined */
+#define SH7751_PCICONF13 0x34 /* PCI Config Reg 13 */
+ #define SH7751_PCICONF13_CPTR 0x000000FF /* PM function pointer */
+/* PCICONF14 is undefined */
+#define SH7751_PCICONF15 0x3C /* PCI Config Reg 15 */
+ #define SH7751_PCICONF15_IPIN 0x000000FF /* Interrupt Pin */
+#define SH7751_PCICONF16 0x40 /* PCI Config Reg 16 */
+ #define SH7751_PCICONF16_PMES 0xF8000000 /* PME Support */
+ #define SH7751_PCICONF16_D2S 0x04000000 /* D2 Support */
+ #define SH7751_PCICONF16_D1S 0x02000000 /* D1 Support */
+ #define SH7751_PCICONF16_DSI 0x00200000 /* Bit Device Init. */
+ #define SH7751_PCICONF16_PMCK 0x00080000 /* Clock for PME req. */
+ #define SH7751_PCICONF16_VER 0x00070000 /* PM Version */
+ #define SH7751_PCICONF16_NIP 0x0000FF00 /* Next Item Pointer */
+ #define SH7751_PCICONF16_CID 0x000000FF /* Capability Identifier */
+#define SH7751_PCICONF17 0x44 /* PCI Config Reg 17 */
+ #define SH7751_PCICONF17_DATA 0xFF000000 /* Data field for PM */
+ #define SH7751_PCICONF17_PMES 0x00800000 /* PME Status */
+ #define SH7751_PCICONF17_DSCL 0x00600000 /* Data Scaling Value */
+ #define SH7751_PCICONF17_DSEL 0x001E0000 /* Data Select */
+ #define SH7751_PCICONF17_PMEN 0x00010000 /* PME Enable */
+ #define SH7751_PCICONF17_PWST 0x00000003 /* Power State */
+/* SH7715 Internal PCI Registers */
+#define SH7751_PCICR 0x100 /* PCI Control Register */
+ #define SH7751_PCICR_PREFIX 0xA5000000 /* CR prefix for write */
+ #define SH7751_PCICR_TRSB 0x00000200 /* Target Read Single */
+ #define SH7751_PCICR_BSWP 0x00000100 /* Target Byte Swap */
+ #define SH7751_PCICR_PLUP 0x00000080 /* Enable PCI Pullup */
+ #define SH7751_PCICR_ARBM 0x00000040 /* PCI Arbitration Mode */
+ #define SH7751_PCICR_MD 0x00000030 /* MD9 and MD10 status */
+ #define SH7751_PCICR_SERR 0x00000008 /* SERR output assert */
+ #define SH7751_PCICR_INTA 0x00000004 /* INTA output assert */
+ #define SH7751_PCICR_PRST 0x00000002 /* PCI Reset Assert */
+ #define SH7751_PCICR_CFIN 0x00000001 /* Central Fun. Init Done */
+#define SH7751_PCILSR0 0x104 /* PCI Local Space Register0 */
+#define SH7751_PCILSR1 0x108 /* PCI Local Space Register1 */
+#define SH7751_PCILAR0 0x10C /* PCI Local Address Register1 */
+#define SH7751_PCILAR1 0x110 /* PCI Local Address Register1 */
+#define SH7751_PCIINT 0x114 /* PCI Interrupt Register */
+ #define SH7751_PCIINT_MLCK 0x00008000 /* Master Lock Error */
+ #define SH7751_PCIINT_TABT 0x00004000 /* Target Abort Error */
+ #define SH7751_PCIINT_TRET 0x00000200 /* Target Retry Error */
+ #define SH7751_PCIINT_MFDE 0x00000100 /* Master Func. Disable Error */
+ #define SH7751_PCIINT_PRTY 0x00000080 /* Address Parity Error */
+ #define SH7751_PCIINT_SERR 0x00000040 /* SERR Detection Error */
+ #define SH7751_PCIINT_TWDP 0x00000020 /* Tgt. Write Parity Error */
+ #define SH7751_PCIINT_TRDP 0x00000010 /* Tgt. Read Parity Error Det. */
+ #define SH7751_PCIINT_MTABT 0x00000008 /* Master-Tgt. Abort Error */
+ #define SH7751_PCIINT_MMABT 0x00000004 /* Master-Master Abort Error */
+ #define SH7751_PCIINT_MWPD 0x00000002 /* Master Write PERR Detect */
+ #define SH7751_PCIINT_MRPD 0x00000002 /* Master Read PERR Detect */
+#define SH7751_PCIINTM 0x118 /* PCI Interrupt Mask Register */
+#define SH7751_PCIALR 0x11C /* Error Address Register */
+#define SH7751_PCICLR 0x120 /* Error Command/Data Register */
+ #define SH7751_PCICLR_MPIO 0x80000000 /* Error Command/Data Register */
+ #define SH7751_PCICLR_MDMA0 0x40000000 /* DMA0 Transfer Error */
+ #define SH7751_PCICLR_MDMA1 0x20000000 /* DMA1 Transfer Error */
+ #define SH7751_PCICLR_MDMA2 0x10000000 /* DMA2 Transfer Error */
+ #define SH7751_PCICLR_MDMA3 0x08000000 /* DMA3 Transfer Error */
+ #define SH7751_PCICLR_TGT 0x04000000 /* Target Transfer Error */
+ #define SH7751_PCICLR_CMDL 0x0000000F /* PCI Command at Error */
+#define SH7751_PCIAINT 0x130 /* Arbiter Interrupt Register */
+ #define SH7751_PCIAINT_MBKN 0x00002000 /* Master Broken Interrupt */
+ #define SH7751_PCIAINT_TBTO 0x00001000 /* Target Bus Time Out */
+ #define SH7751_PCIAINT_MBTO 0x00001000 /* Master Bus Time Out */
+ #define SH7751_PCIAINT_TABT 0x00000008 /* Target Abort */
+ #define SH7751_PCIAINT_MABT 0x00000004 /* Master Abort */
+ #define SH7751_PCIAINT_RDPE 0x00000002 /* Read Data Parity Error */
+ #define SH7751_PCIAINT_WDPE 0x00000002 /* Write Data Parity Error */
+#define SH7751_PCIAINTM 0x134 /* Arbiter Int. Mask Register */
+#define SH7751_PCIBMLR 0x138 /* Error Bus Master Register */
+ #define SH7751_PCIBMLR_REQ4 0x00000010 /* REQ4 bus master at error */
+ #define SH7751_PCIBMLR_REQ3 0x00000008 /* REQ3 bus master at error */
+ #define SH7751_PCIBMLR_REQ2 0x00000004 /* REQ2 bus master at error */
+ #define SH7751_PCIBMLR_REQ1 0x00000002 /* REQ1 bus master at error */
+ #define SH7751_PCIBMLR_REQ0 0x00000001 /* REQ0 bus master at error */
+#define SH7751_PCIDMABT 0x140 /* DMA Transfer Arb. Register */
+ #define SH7751_PCIDMABT_RRBN 0x00000001 /* DMA Arbitor Round-Robin */
+#define SH7751_PCIDPA0 0x180 /* DMA0 Transfer Addr. Register */
+#define SH7751_PCIDLA0 0x184 /* DMA0 Local Addr. Register */
+#define SH7751_PCIDTC0 0x188 /* DMA0 Transfer Cnt. Register */
+#define SH7751_PCIDCR0 0x18C /* DMA0 Control Register */
+ #define SH7751_PCIDCR_ALGN 0x00000600 /* DMA Alignment Mode */
+ #define SH7751_PCIDCR_MAST 0x00000100 /* DMA Termination Type */
+ #define SH7751_PCIDCR_INTM 0x00000080 /* DMA Interrupt Done Mask*/
+ #define SH7751_PCIDCR_INTS 0x00000040 /* DMA Interrupt Done Status */
+ #define SH7751_PCIDCR_LHLD 0x00000020 /* Local Address Control */
+ #define SH7751_PCIDCR_PHLD 0x00000010 /* PCI Address Control*/
+ #define SH7751_PCIDCR_IOSEL 0x00000008 /* PCI Address Space Type */
+ #define SH7751_PCIDCR_DIR 0x00000004 /* DMA Transfer Direction */
+ #define SH7751_PCIDCR_STOP 0x00000002 /* Force DMA Stop */
+ #define SH7751_PCIDCR_STRT 0x00000001 /* DMA Start */
+#define SH7751_PCIDPA1 0x190 /* DMA1 Transfer Addr. Register */
+#define SH7751_PCIDLA1 0x194 /* DMA1 Local Addr. Register */
+#define SH7751_PCIDTC1 0x198 /* DMA1 Transfer Cnt. Register */
+#define SH7751_PCIDCR1 0x19C /* DMA1 Control Register */
+#define SH7751_PCIDPA2 0x1A0 /* DMA2 Transfer Addr. Register */
+#define SH7751_PCIDLA2 0x1A4 /* DMA2 Local Addr. Register */
+#define SH7751_PCIDTC2 0x1A8 /* DMA2 Transfer Cnt. Register */
+#define SH7751_PCIDCR2 0x1AC /* DMA2 Control Register */
+#define SH7751_PCIDPA3 0x1B0 /* DMA3 Transfer Addr. Register */
+#define SH7751_PCIDLA3 0x1B4 /* DMA3 Local Addr. Register */
+#define SH7751_PCIDTC3 0x1B8 /* DMA3 Transfer Cnt. Register */
+#define SH7751_PCIDCR3 0x1BC /* DMA3 Control Register */
+#define SH7751_PCIPAR 0x1C0 /* PIO Address Register */
+ #define SH7751_PCIPAR_CFGEN 0x80000000 /* Configuration Enable */
+ #define SH7751_PCIPAR_BUSNO 0x00FF0000 /* Config. Bus Number */
+ #define SH7751_PCIPAR_DEVNO 0x0000FF00 /* Config. Device Number */
+ #define SH7751_PCIPAR_REGAD 0x000000FC /* Register Address Number */
+#define SH7751_PCIMBR 0x1C4 /* Memory Base Address Register */
+ #define SH7751_PCIMBR_MASK 0xFF000000 /* Memory Space Mask */
+ #define SH7751_PCIMBR_LOCK 0x00000001 /* Lock Memory Space */
+#define SH7751_PCIIOBR 0x1C8 /* I/O Base Address Register */
+ #define SH7751_PCIIOBR_MASK 0xFFFC0000 /* IO Space Mask */
+ #define SH7751_PCIIOBR_LOCK 0x00000001 /* Lock IO Space */
+#define SH7751_PCIPINT 0x1CC /* Power Mgmnt Int. Register */
+ #define SH7751_PCIPINT_D3 0x00000002 /* D3 Pwr Mgmt. Interrupt */
+ #define SH7751_PCIPINT_D0 0x00000001 /* D0 Pwr Mgmt. Interrupt */
+#define SH7751_PCIPINTM 0x1D0 /* Power Mgmnt Mask Register */
+#define SH7751_PCICLKR 0x1D4 /* Clock Ctrl. Register */
+ #define SH7751_PCICLKR_PCSTP 0x00000002 /* PCI Clock Stop */
+ #define SH7751_PCICLKR_BCSTP 0x00000002 /* BCLK Clock Stop */
+/* For definitions of BCR, MCR see ... */
+#define SH7751_PCIBCR1 0x1E0 /* Memory BCR1 Register */
+#define SH7751_PCIBCR2 0x1E4 /* Memory BCR2 Register */
+#define SH7751_PCIWCR1 0x1E8 /* Wait Control 1 Register */
+#define SH7751_PCIWCR2 0x1EC /* Wait Control 2 Register */
+#define SH7751_PCIWCR3 0x1F0 /* Wait Control 3 Register */
+#define SH7751_PCIMCR 0x1F4 /* Memory Control Register */
+#define SH7751_PCIBCR3 0x1f8 /* Memory BCR3 Register */
+#define SH7751_PCIPCTR 0x200 /* Port Control Register */
+ #define SH7751_PCIPCTR_P2EN 0x000400000 /* Port 2 Enable */
+ #define SH7751_PCIPCTR_P1EN 0x000200000 /* Port 1 Enable */
+ #define SH7751_PCIPCTR_P0EN 0x000100000 /* Port 0 Enable */
+ #define SH7751_PCIPCTR_P2UP 0x000000020 /* Port2 Pull Up Enable */
+ #define SH7751_PCIPCTR_P2IO 0x000000010 /* Port2 Output Enable */
+ #define SH7751_PCIPCTR_P1UP 0x000000008 /* Port1 Pull Up Enable */
+ #define SH7751_PCIPCTR_P1IO 0x000000004 /* Port1 Output Enable */
+ #define SH7751_PCIPCTR_P0UP 0x000000002 /* Port0 Pull Up Enable */
+ #define SH7751_PCIPCTR_P0IO 0x000000001 /* Port0 Output Enable */
+#define SH7751_PCIPDTR 0x204 /* Port Data Register */
+ #define SH7751_PCIPDTR_PB5 0x000000020 /* Port 5 Enable */
+ #define SH7751_PCIPDTR_PB4 0x000000010 /* Port 4 Enable */
+ #define SH7751_PCIPDTR_PB3 0x000000008 /* Port 3 Enable */
+ #define SH7751_PCIPDTR_PB2 0x000000004 /* Port 2 Enable */
+ #define SH7751_PCIPDTR_PB1 0x000000002 /* Port 1 Enable */
+ #define SH7751_PCIPDTR_PB0 0x000000001 /* Port 0 Enable */
+#define SH7751_PCIPDR 0x220 /* Port IO Data Register */
+
+/* Memory Control Registers */
+#define SH7751_BCR1 0xFF800000 /* Memory BCR1 Register */
+#define SH7751_BCR2 0xFF800004 /* Memory BCR2 Register */
+#define SH7751_BCR3 0xFF800050 /* Memory BCR3 Register */
+#define SH7751_BCR4 0xFE0A00F0 /* Memory BCR4 Register */
+#define SH7751_WCR1 0xFF800008 /* Wait Control 1 Register */
+#define SH7751_WCR2 0xFF80000C /* Wait Control 2 Register */
+#define SH7751_WCR3 0xFF800010 /* Wait Control 3 Register */
+#define SH7751_MCR 0xFF800014 /* Memory Control Register */
+
+/* General Memory Config Addresses */
+#define SH7751_CS0_BASE_ADDR 0x0
+#define SH7751_MEM_REGION_SIZE 0x04000000
+#define SH7751_CS1_BASE_ADDR (SH7751_CS0_BASE_ADDR + SH7751_MEM_REGION_SIZE)
+#define SH7751_CS2_BASE_ADDR (SH7751_CS1_BASE_ADDR + SH7751_MEM_REGION_SIZE)
+#define SH7751_CS3_BASE_ADDR (SH7751_CS2_BASE_ADDR + SH7751_MEM_REGION_SIZE)
+#define SH7751_CS4_BASE_ADDR (SH7751_CS3_BASE_ADDR + SH7751_MEM_REGION_SIZE)
+#define SH7751_CS5_BASE_ADDR (SH7751_CS4_BASE_ADDR + SH7751_MEM_REGION_SIZE)
+#define SH7751_CS6_BASE_ADDR (SH7751_CS5_BASE_ADDR + SH7751_MEM_REGION_SIZE)
+
+/* General PCI values */
+#define SH7751_PCI_HOST_BRIDGE 0x6
+
+/* Flags */
+#define SH7751_PCIC_NO_RESET 0x0001
+
+/* External functions defined per platform i.e. Big Sur, SE... (these could be routed
+ * through the machine vectors... */
+extern int pcibios_init_platform(void);
+extern int pcibios_map_platform_irq(u8 slot, u8 pin);
+
+struct sh7751_pci_address_space {
+ unsigned long base;
+ unsigned long size;
+};
+
+struct sh7751_pci_address_map {
+ struct sh7751_pci_address_space window0;
+ struct sh7751_pci_address_space window1;
+ unsigned long flags;
+};
+
+/* arch/sh/drivers/pci/pci-sh7751.c */
+extern int sh7751_pcic_init(struct sh7751_pci_address_map *map);
+
+#endif /* _PCI_SH7751_H_ */
+
diff --git a/arch/sh/drivers/pci/pci-st40.c b/arch/sh/drivers/pci/pci-st40.c
new file mode 100644
index 00000000000..cb675213115
--- /dev/null
+++ b/arch/sh/drivers/pci/pci-st40.c
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Support functions for the ST40 PCI hardware.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <asm/pci.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h> /* irqreturn_t */
+
+#include "pci-st40.h"
+
+/* This is in P2 of course */
+#define ST40PCI_BASE_ADDRESS (0xb0000000)
+#define ST40PCI_MEM_ADDRESS (ST40PCI_BASE_ADDRESS+0x0)
+#define ST40PCI_IO_ADDRESS (ST40PCI_BASE_ADDRESS+0x06000000)
+#define ST40PCI_REG_ADDRESS (ST40PCI_BASE_ADDRESS+0x07000000)
+
+#define ST40PCI_REG(x) (ST40PCI_REG_ADDRESS+(ST40PCI_##x))
+#define ST40PCI_REG_INDEXED(reg, index) \
+ (ST40PCI_REG(reg##0) + \
+ ((ST40PCI_REG(reg##1) - ST40PCI_REG(reg##0))*index))
+
+#define ST40PCI_WRITE(reg,val) writel((val),ST40PCI_REG(reg))
+#define ST40PCI_WRITE_SHORT(reg,val) writew((val),ST40PCI_REG(reg))
+#define ST40PCI_WRITE_BYTE(reg,val) writeb((val),ST40PCI_REG(reg))
+#define ST40PCI_WRITE_INDEXED(reg, index, val) \
+ writel((val), ST40PCI_REG_INDEXED(reg, index));
+
+#define ST40PCI_READ(reg) readl(ST40PCI_REG(reg))
+#define ST40PCI_READ_SHORT(reg) readw(ST40PCI_REG(reg))
+#define ST40PCI_READ_BYTE(reg) readb(ST40PCI_REG(reg))
+
+#define ST40PCI_SERR_IRQ 64
+#define ST40PCI_ERR_IRQ 65
+
+
+/* Macros to extract PLL params */
+#define PLL_MDIV(reg) ( ((unsigned)reg) & 0xff )
+#define PLL_NDIV(reg) ( (((unsigned)reg)>>8) & 0xff )
+#define PLL_PDIV(reg) ( (((unsigned)reg)>>16) & 0x3 )
+#define PLL_SETUP(reg) ( (((unsigned)reg)>>19) & 0x1ff )
+
+/* Build up the appropriate settings */
+#define PLL_SET(mdiv,ndiv,pdiv,setup) \
+( ((mdiv)&0xff) | (((ndiv)&0xff)<<8) | (((pdiv)&3)<<16)| (((setup)&0x1ff)<<19))
+
+#define PLLPCICR (0xbb040000+0x10)
+
+#define PLLPCICR_POWERON (1<<28)
+#define PLLPCICR_OUT_EN (1<<29)
+#define PLLPCICR_LOCKSELECT (1<<30)
+#define PLLPCICR_LOCK (1<<31)
+
+
+#define PLL_25MHZ 0x793c8512
+#define PLL_33MHZ PLL_SET(18,88,3,295)
+
+static void pci_set_rbar_region(unsigned int region, unsigned long localAddr,
+ unsigned long pciOffset, unsigned long regionSize);
+
+/*
+ * The pcibios_map_platform_irq function is defined in the appropriate
+ * board specific code and referenced here
+ */
+extern int __init pcibios_map_platform_irq(struct pci_dev *dev, u8 slot, u8 pin);
+
+static __init void SetPCIPLL(void)
+{
+ {
+ /* Lets play with the PLL values */
+ unsigned long pll1cr1;
+ unsigned long mdiv, ndiv, pdiv;
+ unsigned long muxcr;
+ unsigned int muxcr_ratios[4] = { 8, 16, 21, 1 };
+ unsigned int freq;
+
+#define CLKGENA 0xbb040000
+#define CLKGENA_PLL2_MUXCR CLKGENA + 0x48
+ pll1cr1 = ctrl_inl(PLLPCICR);
+ printk("PLL1CR1 %08lx\n", pll1cr1);
+ mdiv = PLL_MDIV(pll1cr1);
+ ndiv = PLL_NDIV(pll1cr1);
+ pdiv = PLL_PDIV(pll1cr1);
+ printk("mdiv %02lx ndiv %02lx pdiv %02lx\n", mdiv, ndiv, pdiv);
+ freq = ((2*27*ndiv)/mdiv) / (1 << pdiv);
+ printk("PLL freq %dMHz\n", freq);
+ muxcr = ctrl_inl(CLKGENA_PLL2_MUXCR);
+ printk("PCI freq %dMhz\n", freq / muxcr_ratios[muxcr & 3]);
+ }
+}
+
+
+struct pci_err {
+ unsigned mask;
+ const char *error_string;
+};
+
+static struct pci_err int_error[]={
+ { INT_MNLTDIM,"MNLTDIM: Master non-lock transfer"},
+ { INT_TTADI, "TTADI: Illegal byte enable in I/O transfer"},
+ { INT_TMTO, "TMTO: Target memory read/write timeout"},
+ { INT_MDEI, "MDEI: Master function disable error"},
+ { INT_APEDI, "APEDI: Address parity error"},
+ { INT_SDI, "SDI: SERR detected"},
+ { INT_DPEITW, "DPEITW: Data parity error target write"},
+ { INT_PEDITR, "PEDITR: PERR detected"},
+ { INT_TADIM, "TADIM: Target abort detected"},
+ { INT_MADIM, "MADIM: Master abort detected"},
+ { INT_MWPDI, "MWPDI: PERR from target at data write"},
+ { INT_MRDPEI, "MRDPEI: Master read data parity error"}
+};
+#define NUM_PCI_INT_ERRS (sizeof(int_error)/sizeof(struct pci_err))
+
+static struct pci_err aint_error[]={
+ { AINT_MBI, "MBI: Master broken"},
+ { AINT_TBTOI, "TBTOI: Target bus timeout"},
+ { AINT_MBTOI, "MBTOI: Master bus timeout"},
+ { AINT_TAI, "TAI: Target abort"},
+ { AINT_MAI, "MAI: Master abort"},
+ { AINT_RDPEI, "RDPEI: Read data parity"},
+ { AINT_WDPE, "WDPE: Write data parity"}
+};
+
+#define NUM_PCI_AINT_ERRS (sizeof(aint_error)/sizeof(struct pci_err))
+
+static void print_pci_errors(unsigned reg,struct pci_err *error,int num_errors)
+{
+ int i;
+
+ for(i=0;i<num_errors;i++) {
+ if(reg & error[i].mask) {
+ printk("%s\n",error[i].error_string);
+ }
+ }
+
+}
+
+
+static char * pci_commands[16]={
+ "Int Ack",
+ "Special Cycle",
+ "I/O Read",
+ "I/O Write",
+ "Reserved",
+ "Reserved",
+ "Memory Read",
+ "Memory Write",
+ "Reserved",
+ "Reserved",
+ "Configuration Read",
+ "Configuration Write",
+ "Memory Read Multiple",
+ "Dual Address Cycle",
+ "Memory Read Line",
+ "Memory Write-and-Invalidate"
+};
+
+static irqreturn_t st40_pci_irq(int irq, void *dev_instance, struct pt_regs *regs)
+{
+ unsigned pci_int, pci_air, pci_cir, pci_aint;
+ static int count=0;
+
+
+ pci_int = ST40PCI_READ(INT);pci_aint = ST40PCI_READ(AINT);
+ pci_cir = ST40PCI_READ(CIR);pci_air = ST40PCI_READ(AIR);
+
+ /* Reset state to stop multiple interrupts */
+ ST40PCI_WRITE(INT, ~0); ST40PCI_WRITE(AINT, ~0);
+
+
+ if(++count>1) return IRQ_HANDLED;
+
+ printk("** PCI ERROR **\n");
+
+ if(pci_int) {
+ printk("** INT register status\n");
+ print_pci_errors(pci_int,int_error,NUM_PCI_INT_ERRS);
+ }
+
+ if(pci_aint) {
+ printk("** AINT register status\n");
+ print_pci_errors(pci_aint,aint_error,NUM_PCI_AINT_ERRS);
+ }
+
+ printk("** Address and command info\n");
+
+ printk("** Command %s : Address 0x%x\n",
+ pci_commands[pci_cir&0xf],pci_air);
+
+ if(pci_cir&CIR_PIOTEM) {
+ printk("CIR_PIOTEM:PIO transfer error for master\n");
+ }
+ if(pci_cir&CIR_RWTET) {
+ printk("CIR_RWTET:Read/Write transfer error for target\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+
+/* Rounds a number UP to the nearest power of two. Used for
+ * sizing the PCI window.
+ */
+static u32 r2p2(u32 num)
+{
+ int i = 31;
+ u32 tmp = num;
+
+ if (num == 0)
+ return 0;
+
+ do {
+ if (tmp & (1 << 31))
+ break;
+ i--;
+ tmp <<= 1;
+ } while (i >= 0);
+
+ tmp = 1 << i;
+ /* If the original number isn't a power of 2, round it up */
+ if (tmp != num)
+ tmp <<= 1;
+
+ return tmp;
+}
+
+static void __init pci_fixup_ide_bases(struct pci_dev *d)
+{
+ int i;
+
+ /*
+ * PCI IDE controllers use non-standard I/O port decoding, respect it.
+ */
+ if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE)
+ return;
+ printk("PCI: IDE base address fixup for %s\n", pci_name(d));
+ for(i=0; i<4; i++) {
+ struct resource *r = &d->resource[i];
+ if ((r->start & ~0x80) == 0x374) {
+ r->start |= 2;
+ r->end = r->start;
+ }
+ }
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases);
+
+int __init st40pci_init(unsigned memStart, unsigned memSize)
+{
+ u32 lsr0;
+
+ SetPCIPLL();
+
+ /* Initialises the ST40 pci subsystem, performing a reset, then programming
+ * up the address space decoders appropriately
+ */
+
+ /* Should reset core here as well methink */
+
+ ST40PCI_WRITE(CR, CR_LOCK_MASK | CR_SOFT_RESET);
+
+ /* Loop while core resets */
+ while (ST40PCI_READ(CR) & CR_SOFT_RESET);
+
+ /* Switch off interrupts */
+ ST40PCI_WRITE(INTM, 0);
+ ST40PCI_WRITE(AINT, 0);
+
+ /* Now, lets reset all the cards on the bus with extreme prejudice */
+ ST40PCI_WRITE(CR, CR_LOCK_MASK | CR_RSTCTL);
+ udelay(250);
+
+ /* Set bus active, take it out of reset */
+ ST40PCI_WRITE(CR, CR_LOCK_MASK | CR_BMAM | CR_CFINT | CR_PFCS | CR_PFE);
+
+ /* The PCI spec says that no access must be made to the bus until 1 second
+ * after reset. This seem ludicrously long, but some delay is needed here
+ */
+ mdelay(1000);
+
+ /* Switch off interrupts */
+ ST40PCI_WRITE(INTM, 0);
+ ST40PCI_WRITE(AINT, 0);
+
+ /* Allow it to be a master */
+
+ ST40PCI_WRITE_SHORT(CSR_CMD,
+ PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+ PCI_COMMAND_IO);
+
+ /* Accesse to the 0xb0000000 -> 0xb6000000 area will go through to 0x10000000 -> 0x16000000
+ * on the PCI bus. This allows a nice 1-1 bus to phys mapping.
+ */
+
+
+ ST40PCI_WRITE(MBR, 0x10000000);
+ /* Always set the max size 128M (actually, it is only 96MB wide) */
+ ST40PCI_WRITE(MBMR, 0x07ff0000);
+
+ /* I/O addresses are mapped at 0xb6000000 -> 0xb7000000. These are changed to 0, to
+ * allow cards that have legacy io such as vga to function correctly. This gives a
+ * maximum of 64K of io/space as only the bottom 16 bits of the address are copied
+ * over to the bus when the transaction is made. 64K of io space is more than enough
+ */
+ ST40PCI_WRITE(IOBR, 0x0);
+ /* Set up the 64K window */
+ ST40PCI_WRITE(IOBMR, 0x0);
+
+ /* Now we set up the mbars so the PCI bus can see the local memory */
+ /* Expose a 256M window starting at PCI address 0... */
+ ST40PCI_WRITE(CSR_MBAR0, 0);
+ ST40PCI_WRITE(LSR0, 0x0fff0001);
+
+ /* ... and set up the initial incomming window to expose all of RAM */
+ pci_set_rbar_region(7, memStart, memStart, memSize);
+
+ /* Maximise timeout values */
+ ST40PCI_WRITE_BYTE(CSR_TRDY, 0xff);
+ ST40PCI_WRITE_BYTE(CSR_RETRY, 0xff);
+ ST40PCI_WRITE_BYTE(CSR_MIT, 0xff);
+
+ ST40PCI_WRITE_BYTE(PERF,PERF_MASTER_WRITE_POSTING);
+
+ return 1;
+}
+
+char * __init pcibios_setup(char *str)
+{
+ return str;
+}
+
+
+#define SET_CONFIG_BITS(bus,devfn,where)\
+ (((bus) << 16) | ((devfn) << 8) | ((where) & ~3) | (bus!=0))
+
+#define CONFIG_CMD(bus, devfn, where) SET_CONFIG_BITS(bus->number,devfn,where)
+
+
+static int CheckForMasterAbort(void)
+{
+ if (ST40PCI_READ(INT) & INT_MADIM) {
+ /* Should we clear config space version as well ??? */
+ ST40PCI_WRITE(INT, INT_MADIM);
+ ST40PCI_WRITE_SHORT(CSR_STATUS, 0);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Write to config register */
+static int st40pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 * val)
+{
+ ST40PCI_WRITE(PAR, CONFIG_CMD(bus, devfn, where));
+ switch (size) {
+ case 1:
+ *val = (u8)ST40PCI_READ_BYTE(PDR + (where & 3));
+ break;
+ case 2:
+ *val = (u16)ST40PCI_READ_SHORT(PDR + (where & 2));
+ break;
+ case 4:
+ *val = ST40PCI_READ(PDR);
+ break;
+ }
+
+ if (CheckForMasterAbort()){
+ switch (size) {
+ case 1:
+ *val = (u8)0xff;
+ break;
+ case 2:
+ *val = (u16)0xffff;
+ break;
+ case 4:
+ *val = 0xffffffff;
+ break;
+ }
+ }
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int st40pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val)
+{
+ ST40PCI_WRITE(PAR, CONFIG_CMD(bus, devfn, where));
+
+ switch (size) {
+ case 1:
+ ST40PCI_WRITE_BYTE(PDR + (where & 3), (u8)val);
+ break;
+ case 2:
+ ST40PCI_WRITE_SHORT(PDR + (where & 2), (u16)val);
+ break;
+ case 4:
+ ST40PCI_WRITE(PDR, val);
+ break;
+ }
+
+ CheckForMasterAbort();
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+struct pci_ops st40pci_config_ops = {
+ .read = st40pci_read,
+ .write = st40pci_write,
+};
+
+
+/* Everything hangs off this */
+static struct pci_bus *pci_root_bus;
+
+
+static u8 __init no_swizzle(struct pci_dev *dev, u8 * pin)
+{
+ return PCI_SLOT(dev->devfn);
+}
+
+
+static int __init pcibios_init(void)
+{
+ extern unsigned long memory_start, memory_end;
+
+ printk(KERN_ALERT "pci-st40.c: pcibios_init\n");
+
+ if (sh_mv.mv_init_pci != NULL) {
+ sh_mv.mv_init_pci();
+ }
+
+ /* The pci subsytem needs to know where memory is and how much
+ * of it there is. I've simply made these globals. A better mechanism
+ * is probably needed.
+ */
+ st40pci_init(PHYSADDR(memory_start),
+ PHYSADDR(memory_end) - PHYSADDR(memory_start));
+
+ if (request_irq(ST40PCI_ERR_IRQ, st40_pci_irq,
+ SA_INTERRUPT, "st40pci", NULL)) {
+ printk(KERN_ERR "st40pci: Cannot hook interrupt\n");
+ return -EIO;
+ }
+
+ /* Enable the PCI interrupts on the device */
+ ST40PCI_WRITE(INTM, ~0);
+ ST40PCI_WRITE(AINT, ~0);
+
+ /* Map the io address apprioately */
+#ifdef CONFIG_HD64465
+ hd64465_port_map(PCIBIOS_MIN_IO, (64 * 1024) - PCIBIOS_MIN_IO + 1,
+ ST40_IO_ADDR + PCIBIOS_MIN_IO, 0);
+#endif
+
+ /* ok, do the scan man */
+ pci_root_bus = pci_scan_bus(0, &st40pci_config_ops, NULL);
+ pci_assign_unassigned_resources();
+ pci_fixup_irqs(no_swizzle, pcibios_map_platform_irq);
+
+ return 0;
+}
+
+subsys_initcall(pcibios_init);
+
+void __init pcibios_fixup_bus(struct pci_bus *bus)
+{
+}
+
+/*
+ * Publish a region of local address space over the PCI bus
+ * to other devices.
+ */
+static void pci_set_rbar_region(unsigned int region, unsigned long localAddr,
+ unsigned long pciOffset, unsigned long regionSize)
+{
+ unsigned long mask;
+
+ if (region > 7)
+ return;
+
+ if (regionSize > (512 * 1024 * 1024))
+ return;
+
+ mask = r2p2(regionSize) - 0x10000;
+
+ /* Diable the region (in case currently in use, should never happen) */
+ ST40PCI_WRITE_INDEXED(RSR, region, 0);
+
+ /* Start of local address space to publish */
+ ST40PCI_WRITE_INDEXED(RLAR, region, PHYSADDR(localAddr) );
+
+ /* Start of region in PCI address space as an offset from MBAR0 */
+ ST40PCI_WRITE_INDEXED(RBAR, region, pciOffset);
+
+ /* Size of region */
+ ST40PCI_WRITE_INDEXED(RSR, region, mask | 1);
+}
+
diff --git a/arch/sh/drivers/pci/pci-st40.h b/arch/sh/drivers/pci/pci-st40.h
new file mode 100644
index 00000000000..d729e0c2d5f
--- /dev/null
+++ b/arch/sh/drivers/pci/pci-st40.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2001 David J. Mckay (david.mckay@st.com)
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Defintions for the ST40 PCI hardware.
+ */
+
+#ifndef __PCI_ST40_H__
+#define __PCI_ST40_H__
+
+#define ST40PCI_VCR_STATUS 0x00
+
+#define ST40PCI_VCR_VERSION 0x08
+
+#define ST40PCI_CR 0x10
+
+#define CR_SOFT_RESET (1<<12)
+#define CR_PFCS (1<<11)
+#define CR_PFE (1<<9)
+#define CR_BMAM (1<<6)
+#define CR_HOST (1<<5)
+#define CR_CLKEN (1<<4)
+#define CR_SOCS (1<<3)
+#define CR_IOCS (1<<2)
+#define CR_RSTCTL (1<<1)
+#define CR_CFINT (1<<0)
+#define CR_LOCK_MASK 0x5a000000
+
+
+#define ST40PCI_LSR0 0X14
+#define ST40PCI_LAR0 0x1c
+
+#define ST40PCI_INT 0x24
+#define INT_MNLTDIM (1<<15)
+#define INT_TTADI (1<<14)
+#define INT_TMTO (1<<9)
+#define INT_MDEI (1<<8)
+#define INT_APEDI (1<<7)
+#define INT_SDI (1<<6)
+#define INT_DPEITW (1<<5)
+#define INT_PEDITR (1<<4)
+#define INT_TADIM (1<<3)
+#define INT_MADIM (1<<2)
+#define INT_MWPDI (1<<1)
+#define INT_MRDPEI (1<<0)
+
+
+#define ST40PCI_INTM 0x28
+#define ST40PCI_AIR 0x2c
+
+#define ST40PCI_CIR 0x30
+#define CIR_PIOTEM (1<<31)
+#define CIR_RWTET (1<<26)
+
+#define ST40PCI_AINT 0x40
+#define AINT_MBI (1<<13)
+#define AINT_TBTOI (1<<12)
+#define AINT_MBTOI (1<<11)
+#define AINT_TAI (1<<3)
+#define AINT_MAI (1<<2)
+#define AINT_RDPEI (1<<1)
+#define AINT_WDPE (1<<0)
+
+#define ST40PCI_AINTM 0x44
+#define ST40PCI_BMIR 0x48
+#define ST40PCI_PAR 0x4c
+#define ST40PCI_MBR 0x50
+#define ST40PCI_IOBR 0x54
+#define ST40PCI_PINT 0x58
+#define ST40PCI_PINTM 0x5c
+#define ST40PCI_MBMR 0x70
+#define ST40PCI_IOBMR 0x74
+#define ST40PCI_PDR 0x78
+
+/* H8 specific registers start here */
+#define ST40PCI_WCBAR 0x7c
+#define ST40PCI_LOCCFG_UNLOCK 0x34
+
+#define ST40PCI_RBAR0 0x100
+#define ST40PCI_RSR0 0x104
+#define ST40PCI_RLAR0 0x108
+
+#define ST40PCI_RBAR1 0x110
+#define ST40PCI_RSR1 0x114
+#define ST40PCI_RLAR1 0x118
+
+
+#define ST40PCI_RBAR2 0x120
+#define ST40PCI_RSR2 0x124
+#define ST40PCI_RLAR2 0x128
+
+#define ST40PCI_RBAR3 0x130
+#define ST40PCI_RSR3 0x134
+#define ST40PCI_RLAR3 0x138
+
+#define ST40PCI_RBAR4 0x140
+#define ST40PCI_RSR4 0x144
+#define ST40PCI_RLAR4 0x148
+
+#define ST40PCI_RBAR5 0x150
+#define ST40PCI_RSR5 0x154
+#define ST40PCI_RLAR5 0x158
+
+#define ST40PCI_RBAR6 0x160
+#define ST40PCI_RSR6 0x164
+#define ST40PCI_RLAR6 0x168
+
+#define ST40PCI_RBAR7 0x170
+#define ST40PCI_RSR7 0x174
+#define ST40PCI_RLAR7 0x178
+
+
+#define ST40PCI_RBAR(n) (0x100+(0x10*(n)))
+#define ST40PCI_RSR(n) (0x104+(0x10*(n)))
+#define ST40PCI_RLAR(n) (0x108+(0x10*(n)))
+
+#define ST40PCI_PERF 0x80
+#define PERF_MASTER_WRITE_POSTING (1<<4)
+/* H8 specific registers end here */
+
+
+/* These are configs space registers */
+#define ST40PCI_CSR_VID 0x10000
+#define ST40PCI_CSR_DID 0x10002
+#define ST40PCI_CSR_CMD 0x10004
+#define ST40PCI_CSR_STATUS 0x10006
+#define ST40PCI_CSR_MBAR0 0x10010
+#define ST40PCI_CSR_TRDY 0x10040
+#define ST40PCI_CSR_RETRY 0x10041
+#define ST40PCI_CSR_MIT 0x1000d
+
+#define ST40_IO_ADDR 0xb6000000
+
+#endif /* __PCI_ST40_H__ */
diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c
new file mode 100644
index 00000000000..c1669905abe
--- /dev/null
+++ b/arch/sh/drivers/pci/pci.c
@@ -0,0 +1,155 @@
+/* arch/sh/kernel/pci.c
+ * $Id: pci.c,v 1.1 2003/08/24 19:15:45 lethal Exp $
+ *
+ * Copyright (c) 2002 M. R. Brown <mrbrown@linux-sh.org>
+ *
+ *
+ * These functions are collected here to reduce duplication of common
+ * code amongst the many platform-specific PCI support code files.
+ *
+ * These routines require the following board-specific routines:
+ * void pcibios_fixup_irqs();
+ *
+ * See include/asm-sh/pci.h for more information.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+static int __init pcibios_init(void)
+{
+ struct pci_channel *p;
+ struct pci_bus *bus;
+ int busno;
+
+#ifdef CONFIG_PCI_AUTO
+ /* assign resources */
+ busno = 0;
+ for (p = board_pci_channels; p->pci_ops != NULL; p++) {
+ busno = pciauto_assign_resources(busno, p) + 1;
+ }
+#endif
+
+ /* scan the buses */
+ busno = 0;
+ for (p= board_pci_channels; p->pci_ops != NULL; p++) {
+ bus = pci_scan_bus(busno, p->pci_ops, p);
+ busno = bus->subordinate+1;
+ }
+
+ /* board-specific fixups */
+ pcibios_fixup_irqs();
+
+ return 0;
+}
+
+subsys_initcall(pcibios_init);
+
+void
+pcibios_update_resource(struct pci_dev *dev, struct resource *root,
+ struct resource *res, int resource)
+{
+ u32 new, check;
+ int reg;
+
+ new = res->start | (res->flags & PCI_REGION_FLAG_MASK);
+ if (resource < 6) {
+ reg = PCI_BASE_ADDRESS_0 + 4*resource;
+ } else if (resource == PCI_ROM_RESOURCE) {
+ res->flags |= IORESOURCE_ROM_ENABLE;
+ new |= PCI_ROM_ADDRESS_ENABLE;
+ reg = dev->rom_base_reg;
+ } else {
+ /* Somebody might have asked allocation of a non-standard resource */
+ return;
+ }
+
+ pci_write_config_dword(dev, reg, new);
+ pci_read_config_dword(dev, reg, &check);
+ if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) {
+ printk(KERN_ERR "PCI: Error while updating region "
+ "%s/%d (%08x != %08x)\n", pci_name(dev), resource,
+ new, check);
+ }
+}
+
+void pcibios_align_resource(void *data, struct resource *res,
+ unsigned long size, unsigned long align)
+ __attribute__ ((weak));
+
+/*
+ * 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.
+ */
+void pcibios_align_resource(void *data, struct resource *res,
+ unsigned long size, unsigned long align)
+{
+ if (res->flags & IORESOURCE_IO) {
+ unsigned long start = res->start;
+
+ if (start & 0x300) {
+ start = (start + 0x3ff) & ~0x3ff;
+ res->start = start;
+ }
+ }
+}
+
+int pcibios_enable_device(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++) {
+ 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) {
+ printk(KERN_INFO "PCI: Enabling device %s (%04x -> %04x)\n",
+ pci_name(dev), old_cmd, cmd);
+ pci_write_config_word(dev, PCI_COMMAND, cmd);
+ }
+ return 0;
+}
+
+/*
+ * If we set up a device for bus mastering, we need to check and set
+ * the latency timer as it may not be properly set.
+ */
+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;
+ printk(KERN_INFO "PCI: Setting latency timer of device %s to %d\n", pci_name(dev), lat);
+ pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
+}
+
+void __init pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+}