/* $Id: sbus.c,v 1.19 2002/01/23 11:27:32 davem Exp $
* sbus.c: UltraSparc SBUS controller support.
*
* Copyright (C) 1999 David S. Miller (davem@redhat.com)
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <asm/page.h>
#include <asm/sbus.h>
#include <asm/io.h>
#include <asm/upa.h>
#include <asm/cache.h>
#include <asm/dma.h>
#include <asm/irq.h>
#include <asm/prom.h>
#include <asm/starfire.h>
#include "iommu_common.h"
/* These should be allocated on an SMP_CACHE_BYTES
* aligned boundary for optimal performance.
*
* On SYSIO, using an 8K page size we have 1GB of SBUS
* DMA space mapped. We divide this space into equally
* sized clusters. We allocate a DMA mapping from the
* cluster that matches the order of the allocation, or
* if the order is greater than the number of clusters,
* we try to allocate from the last cluster.
*/
#define NCLUSTERS 8UL
#define ONE_GIG (1UL * 1024UL * 1024UL * 1024UL)
#define CLUSTER_SIZE (ONE_GIG / NCLUSTERS)
#define CLUSTER_MASK (CLUSTER_SIZE - 1)
#define CLUSTER_NPAGES (CLUSTER_SIZE >> IO_PAGE_SHIFT)
#define MAP_BASE ((u32)0xc0000000)
struct sbus_iommu {
/*0x00*/spinlock_t lock;
/*0x08*/iopte_t *page_table;
/*0x10*/unsigned long strbuf_regs;
/*0x18*/unsigned long iommu_regs;
/*0x20*/unsigned long sbus_control_reg;
/*0x28*/volatile unsigned long strbuf_flushflag;
/* If NCLUSTERS is ever decresed to 4 or lower,
* you must increase the size of the type of
* these counters. You have been duly warned. -DaveM
*/
/*0x30*/struct {
u16 next;
u16 flush;
} alloc_info[NCLUSTERS];
/* The lowest used consistent mapping entry. Since
* we allocate consistent maps out of cluster 0 this
* is relative to the beginning of closter 0.
*/
/*0x50*/u32 lowest_consistent_map;
};
/* Offsets from iommu_regs */
#define SYSIO_IOMMUREG_BASE 0x2400UL
#define IOMMU_CONTROL (0x2400UL - 0x2400UL) /* IOMMU control register */
#define IOMMU_TSBBASE (0x2408UL - 0x2400UL) /* TSB base address register */
#define IOMMU_FLUSH (0x2410UL - 0x2400UL) /* IOMMU flush register */
#define IOMMU_VADIAG (0x4400UL - 0x2400UL) /* SBUS virtual address diagnostic */
#define IOMMU_TAGCMP (0x4408UL - 0x2400UL) /* TLB tag compare diagnostics */
#define IOMMU_LRUDIAG (0x4500UL - 0x2400UL) /* IOMMU LRU queue diagnostics */
#define IOMMU_TAGDIAG (0x4580UL - 0x2400UL) /* TLB tag diagnostics */
#define IOMMU_DRAMDIAG (0x4600UL - 0x2400UL) /* TLB data RAM diagnostics */
#define IOMMU_DRAM_VALID (1UL << 30UL)
static void __iommu_flushall(struct sbus_iommu *iommu)
{
unsigned long tag = iommu->iommu_regs + IOMMU_TAGDIAG;
int entry;
for (entry = 0; entry < 16; entry++) {
upa_writeq(0, tag);
tag += 8UL;
}
upa_readq(iommu->sbus_control_reg);
for (entry = 0; entry < NCLUSTERS; entry++) {
iommu->alloc_info[entry].flush =
iommu->alloc_info[entry].next;
}
}
static void iommu_flush(struct sbus_iommu *iommu, u32 base, unsigned long npages)
{
while (npages--)
upa_writeq(base + (npages << IO_PAGE_SHIFT),
iommu->iommu_regs + IOMMU_FLUSH);
upa_readq(iommu->sbus_control_reg);
}
/* Offsets from strbuf_regs */
#define SYSIO_STRBUFREG_BASE 0x2800UL
#define STRBUF_CONTROL (0x2800UL - 0x2800UL) /* Control */
#define STRBUF_PFLUSH (0x2808UL - 0x2800UL) /* Page flush/invalidate */
#define STRBUF_FSYNC (0x2810UL - 0x2800UL) /* Flush synchronization */
#define STRBUF_DRAMDIAG (0x5000UL - 0x2800UL) /* data RAM diagnostic */
#define STRBUF_ERRDIAG (0x5400UL - 0x2800UL) /* error status diagnostics */
#define STRBUF_PTAGDIAG (0x5800UL - 0x2800UL) /* Page tag diagnostics */
#define STRBUF_LTAGDIAG (0x5900UL - 0x2800UL) /* Line tag diagnostics */
#define STRBUF_TAG_VALID 0x02UL
static void