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:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

7
arch/sh/drivers/Makefile Normal file
View File

@@ -0,0 +1,7 @@
#
# Makefile for the Linux SuperH-specific device drivers.
#
obj-$(CONFIG_PCI) += pci/
obj-$(CONFIG_SH_DMA) += dma/

View 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

View 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

View 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, &registered_dmac_list) {
struct dma_info *info = list_entry(pos, struct dma_info, list);
total += info->nr_channels;
if (chan > total)
continue;
return info;
}
return NULL;
}
struct dma_channel *get_dma_channel(unsigned int chan)
{
struct dma_info *info = get_dma_info(chan);
if (!info)
return ERR_PTR(-EINVAL);
return info->channels + chan;
}
int get_dma_residue(unsigned int chan)
{
struct dma_info *info = get_dma_info(chan);
struct dma_channel *channel = &info->channels[chan];
if (info->ops->get_residue)
return info->ops->get_residue(channel);
return 0;
}
int request_dma(unsigned int chan, const char *dev_id)
{
struct dma_info *info = get_dma_info(chan);
struct dma_channel *channel = &info->channels[chan];
down(&channel->sem);
if (!info->ops || chan >= MAX_DMA_CHANNELS) {
up(&channel->sem);
return -EINVAL;
}
atomic_set(&channel->busy, 1);
strlcpy(channel->dev_id, dev_id, sizeof(channel->dev_id));
up(&channel->sem);
if (info->ops->request)
return info->ops->request(channel);
return 0;
}
void free_dma(unsigned int chan)
{
struct dma_info *info = get_dma_info(chan);
struct dma_channel *channel = &info->channels[chan];
if (info->ops->free)
info->ops->free(channel);
atomic_set(&channel->busy, 0);
}
void dma_wait_for_completion(unsigned int chan)
{
struct dma_info *info = get_dma_info(chan);
struct dma_channel *channel = &info->channels[chan];
if (channel->flags & DMA_TEI_CAPABLE) {
wait_event(channel->wait_queue,
(info->ops->get_residue(channel) == 0));
return;
}
while (info->ops->get_residue(channel))
cpu_relax();
}
void dma_configure_channel(unsigned int chan, unsigned long flags)
{
struct dma_info *info = get_dma_info(chan);
struct dma_channel *channel = &info->channels[chan];
if (info->ops->configure)
info->ops->configure(channel, flags);
}
int dma_xfer(unsigned int chan, unsigned long from,
unsigned long to, size_t size, unsigned int mode)
{
struct dma_info *info = get_dma_info(chan);
struct dma_channel *channel = &info->channels[chan];
channel->sar = from;
channel->dar = to;
channel->count = size;
channel->mode = mode;
return info->ops->xfer(channel);
}
#ifdef CONFIG_PROC_FS
static int dma_read_proc(char *buf, char **start, off_t off,
int len, int *eof, void *data)
{
struct list_head *pos, *tmp;
char *p = buf;
if (list_empty(&registered_dmac_list))
return 0;
/*
* Iterate over each registered DMAC
*/
list_for_each_safe(pos, tmp, &registered_dmac_list) {
struct dma_info *info = list_entry(pos, struct dma_info, list);
int i;
/*
* Iterate over each channel
*/
for (i = 0; i < info->nr_channels; i++) {
struct dma_channel *channel = info->channels + i;
if (!(channel->flags & DMA_CONFIGURED))
continue;
p += sprintf(p, "%2d: %14s %s\n", i,
info->name, channel->dev_id);
}
}
return p - buf;
}
#endif
int __init register_dmac(struct dma_info *info)
{
int i;
INIT_LIST_HEAD(&info->list);
printk(KERN_INFO "DMA: Registering %s handler (%d channel%s).\n",
info->name, info->nr_channels,
info->nr_channels > 1 ? "s" : "");
BUG_ON((info->flags & DMAC_CHANNELS_CONFIGURED) && !info->channels);
/*
* Don't touch pre-configured channels
*/
if (!(info->flags & DMAC_CHANNELS_CONFIGURED)) {
unsigned int size;
size = sizeof(struct dma_channel) * info->nr_channels;
info->channels = kmalloc(size, GFP_KERNEL);
if (!info->channels)
return -ENOMEM;
memset(info->channels, 0, size);
}
for (i = 0; i < info->nr_channels; i++) {
struct dma_channel *chan = info->channels + i;
chan->chan = i;
memcpy(chan->dev_id, "Unused", 7);
if (info->flags & DMAC_CHANNELS_TEI_CAPABLE)
chan->flags |= DMA_TEI_CAPABLE;
init_MUTEX(&chan->sem);
init_waitqueue_head(&chan->wait_queue);
#ifdef CONFIG_SYSFS
dma_create_sysfs_files(chan);
#endif
}
list_add(&info->list, &registered_dmac_list);
return 0;
}
void __exit unregister_dmac(struct dma_info *info)
{
if (!(info->flags & DMAC_CHANNELS_CONFIGURED))
kfree(info->channels);
list_del(&info->list);
}
static int __init dma_api_init(void)
{
printk("DMA: Registering DMA API.\n");
#ifdef CONFIG_PROC_FS
create_proc_read_entry("dma", 0, 0, dma_read_proc, 0);
#endif
return 0;
}
subsys_initcall(dma_api_init);
MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
MODULE_DESCRIPTION("DMA API for SuperH");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(request_dma);
EXPORT_SYMBOL(free_dma);
EXPORT_SYMBOL(register_dmac);
EXPORT_SYMBOL(get_dma_residue);
EXPORT_SYMBOL(get_dma_info);
EXPORT_SYMBOL(get_dma_channel);
EXPORT_SYMBOL(dma_xfer);
EXPORT_SYMBOL(dma_wait_for_completion);
EXPORT_SYMBOL(dma_configure_channel);

View 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");

View 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);

View 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");

View 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");

View 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 */

View 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;
}

View 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.

View 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

View 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;
}

View 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);
}
}

View 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;
}

View 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);
}

View 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;
}

View 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;
}

View 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);
}

View 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 },
};

View 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 .. */
}

View 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);
}

View 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);
}

View 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_ */

View 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);
}

View 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
View 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);
}