mirror of
https://github.com/genesi/linux-legacy.git
synced 2026-05-17 15:31:57 +00:00
Linux-2.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!
This commit is contained in:
7
arch/sh/drivers/Makefile
Normal file
7
arch/sh/drivers/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# Makefile for the Linux SuperH-specific device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_PCI) += pci/
|
||||
obj-$(CONFIG_SH_DMA) += dma/
|
||||
|
||||
55
arch/sh/drivers/dma/Kconfig
Normal file
55
arch/sh/drivers/dma/Kconfig
Normal file
@@ -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
|
||||
|
||||
9
arch/sh/drivers/dma/Makefile
Normal file
9
arch/sh/drivers/dma/Makefile
Normal file
@@ -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
|
||||
|
||||
292
arch/sh/drivers/dma/dma-api.c
Normal file
292
arch/sh/drivers/dma/dma-api.c
Normal file
@@ -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, ®istered_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(®istered_dmac_list))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Iterate over each registered DMAC
|
||||
*/
|
||||
list_for_each_safe(pos, tmp, ®istered_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, ®istered_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);
|
||||
|
||||
171
arch/sh/drivers/dma/dma-g2.c
Normal file
171
arch/sh/drivers/dma/dma-g2.c
Normal file
@@ -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");
|
||||
|
||||
106
arch/sh/drivers/dma/dma-isa.c
Normal file
106
arch/sh/drivers/dma/dma-isa.c
Normal file
@@ -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);
|
||||
|
||||
109
arch/sh/drivers/dma/dma-pvr2.c
Normal file
109
arch/sh/drivers/dma/dma-pvr2.c
Normal file
@@ -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");
|
||||
|
||||
267
arch/sh/drivers/dma/dma-sh.c
Normal file
267
arch/sh/drivers/dma/dma-sh.c
Normal file
@@ -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");
|
||||
|
||||
52
arch/sh/drivers/dma/dma-sh.h
Normal file
52
arch/sh/drivers/dma/dma-sh.h
Normal file
@@ -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 */
|
||||
|
||||
133
arch/sh/drivers/dma/dma-sysfs.c
Normal file
133
arch/sh/drivers/dma/dma-sysfs.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
41
arch/sh/drivers/pci/Kconfig
Normal file
41
arch/sh/drivers/pci/Kconfig
Normal file
@@ -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.
|
||||
|
||||
16
arch/sh/drivers/pci/Makefile
Normal file
16
arch/sh/drivers/pci/Makefile
Normal file
@@ -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
|
||||
71
arch/sh/drivers/pci/dma-dreamcast.c
Normal file
71
arch/sh/drivers/pci/dma-dreamcast.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
81
arch/sh/drivers/pci/fixups-dreamcast.c
Normal file
81
arch/sh/drivers/pci/fixups-dreamcast.c
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
43
arch/sh/drivers/pci/fixups-rts7751r2d.c
Normal file
43
arch/sh/drivers/pci/fixups-rts7751r2d.c
Normal file
@@ -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;
|
||||
}
|
||||
61
arch/sh/drivers/pci/fixups-sh03.c
Normal file
61
arch/sh/drivers/pci/fixups-sh03.c
Normal file
@@ -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);
|
||||
}
|
||||
88
arch/sh/drivers/pci/ops-bigsur.c
Normal file
88
arch/sh/drivers/pci/ops-bigsur.c
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
169
arch/sh/drivers/pci/ops-dreamcast.c
Normal file
169
arch/sh/drivers/pci/ops-dreamcast.c
Normal file
@@ -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;
|
||||
}
|
||||
79
arch/sh/drivers/pci/ops-rts7751r2d.c
Normal file
79
arch/sh/drivers/pci/ops-rts7751r2d.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
45
arch/sh/drivers/pci/ops-sh03.c
Normal file
45
arch/sh/drivers/pci/ops-sh03.c
Normal file
@@ -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 },
|
||||
};
|
||||
|
||||
102
arch/sh/drivers/pci/ops-snapgear.c
Normal file
102
arch/sh/drivers/pci/ops-snapgear.c
Normal file
@@ -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 .. */
|
||||
}
|
||||
|
||||
555
arch/sh/drivers/pci/pci-auto.c
Normal file
555
arch/sh/drivers/pci/pci-auto.c
Normal file
@@ -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);
|
||||
}
|
||||
417
arch/sh/drivers/pci/pci-sh7751.c
Normal file
417
arch/sh/drivers/pci/pci-sh7751.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
303
arch/sh/drivers/pci/pci-sh7751.h
Normal file
303
arch/sh/drivers/pci/pci-sh7751.h
Normal file
@@ -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_ */
|
||||
|
||||
509
arch/sh/drivers/pci/pci-st40.c
Normal file
509
arch/sh/drivers/pci/pci-st40.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
136
arch/sh/drivers/pci/pci-st40.h
Normal file
136
arch/sh/drivers/pci/pci-st40.h
Normal file
@@ -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__ */
|
||||
155
arch/sh/drivers/pci/pci.c
Normal file
155
arch/sh/drivers/pci/pci.c
Normal file
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user