dvma.c 4.56 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
Linus Torvalds's avatar
Linus Torvalds committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Virtual DMA allocation
 *
 * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
 *
 * 11/26/2000 -- disabled the existing code because it didn't work for
 * me in 2.4.  Replaced with a significantly more primitive version
 * similar to the sun3 code.  the old functionality was probably more
 * desirable, but....   -- Sam Creasey (sammy@oh.verio.com)
 *
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/mm.h>
18
#include <linux/memblock.h>
Linus Torvalds's avatar
Linus Torvalds committed
19 20 21 22 23 24
#include <linux/vmalloc.h>

#include <asm/sun3x.h>
#include <asm/dvma.h>
#include <asm/io.h>
#include <asm/page.h>
25
#include <asm/tlbflush.h>
Linus Torvalds's avatar
Linus Torvalds committed
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

/* IOMMU support */

#define IOMMU_ADDR_MASK            0x03ffe000
#define IOMMU_CACHE_INHIBIT        0x00000040
#define IOMMU_FULL_BLOCK           0x00000020
#define IOMMU_MODIFIED             0x00000010
#define IOMMU_USED                 0x00000008
#define IOMMU_WRITE_PROTECT        0x00000004
#define IOMMU_DT_MASK              0x00000003
#define IOMMU_DT_INVALID           0x00000000
#define IOMMU_DT_VALID             0x00000001
#define IOMMU_DT_BAD               0x00000002


static volatile unsigned long *iommu_pte = (unsigned long *)SUN3X_IOMMU;


#define dvma_entry_paddr(index)		(iommu_pte[index] & IOMMU_ADDR_MASK)
#define dvma_entry_vaddr(index,paddr)	((index << DVMA_PAGE_SHIFT) |  \
					 (paddr & (DVMA_PAGE_SIZE-1)))
#if 0
#define dvma_entry_set(index,addr)	(iommu_pte[index] =            \
					    (addr & IOMMU_ADDR_MASK) | \
				             IOMMU_DT_VALID | IOMMU_CACHE_INHIBIT)
#else
#define dvma_entry_set(index,addr)	(iommu_pte[index] =            \
					    (addr & IOMMU_ADDR_MASK) | \
				             IOMMU_DT_VALID)
#endif
#define dvma_entry_clr(index)		(iommu_pte[index] = IOMMU_DT_INVALID)
#define dvma_entry_hash(addr)		((addr >> DVMA_PAGE_SHIFT) ^ \
					 ((addr & 0x03c00000) >>     \
						(DVMA_PAGE_SHIFT+4)))

#ifdef DEBUG
/* code to print out a dvma mapping for debugging purposes */
void dvma_print (unsigned long dvma_addr)
{

66
	unsigned long index;
Linus Torvalds's avatar
Linus Torvalds committed
67

68
	index = dvma_addr >> DVMA_PAGE_SHIFT;
Linus Torvalds's avatar
Linus Torvalds committed
69

70 71
	pr_info("idx %lx dvma_addr %08lx paddr %08lx\n", index, dvma_addr,
		dvma_entry_paddr(index));
Linus Torvalds's avatar
Linus Torvalds committed
72 73 74 75 76 77 78 79 80 81
}
#endif


/* create a virtual mapping for a page assigned within the IOMMU
   so that the cpu can reach it easily */
inline int dvma_map_cpu(unsigned long kaddr,
			       unsigned long vaddr, int len)
{
	pgd_t *pgd;
82 83
	p4d_t *p4d;
	pud_t *pud;
Linus Torvalds's avatar
Linus Torvalds committed
84 85 86 87 88 89 90 91
	unsigned long end;
	int ret = 0;

	kaddr &= PAGE_MASK;
	vaddr &= PAGE_MASK;

	end = PAGE_ALIGN(vaddr + len);

92
	pr_debug("dvma: mapping kern %08lx to virt %08lx\n", kaddr, vaddr);
Linus Torvalds's avatar
Linus Torvalds committed
93
	pgd = pgd_offset_k(vaddr);
94 95
	p4d = p4d_offset(pgd, vaddr);
	pud = pud_offset(p4d, vaddr);
Linus Torvalds's avatar
Linus Torvalds committed
96 97 98 99 100

	do {
		pmd_t *pmd;
		unsigned long end2;

101
		if((pmd = pmd_alloc(&init_mm, pud, vaddr)) == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
102 103 104 105 106 107 108 109 110 111 112 113 114
			ret = -ENOMEM;
			goto out;
		}

		if((end & PGDIR_MASK) > (vaddr & PGDIR_MASK))
			end2 = (vaddr + (PGDIR_SIZE-1)) & PGDIR_MASK;
		else
			end2 = end;

		do {
			pte_t *pte;
			unsigned long end3;

115
			if((pte = pte_alloc_kernel(pmd, vaddr)) == NULL) {
Linus Torvalds's avatar
Linus Torvalds committed
116 117 118 119 120 121 122 123 124 125
				ret = -ENOMEM;
				goto out;
			}

			if((end2 & PMD_MASK) > (vaddr & PMD_MASK))
				end3 = (vaddr + (PMD_SIZE-1)) & PMD_MASK;
			else
				end3 = end2;

			do {
126 127
				pr_debug("mapping %08lx phys to %08lx\n",
					 __pa(kaddr), vaddr);
Linus Torvalds's avatar
Linus Torvalds committed
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
				set_pte(pte, pfn_pte(virt_to_pfn(kaddr),
						     PAGE_KERNEL));
				pte++;
				kaddr += PAGE_SIZE;
				vaddr += PAGE_SIZE;
			} while(vaddr < end3);

		} while(vaddr < end2);

	} while(vaddr < end);

	flush_tlb_all();

 out:
	return ret;
}


inline int dvma_map_iommu(unsigned long kaddr, unsigned long baddr,
				 int len)
{
	unsigned long end, index;

	index = baddr >> DVMA_PAGE_SHIFT;
	end = ((baddr+len) >> DVMA_PAGE_SHIFT);

	if(len & ~DVMA_PAGE_MASK)
		end++;

	for(; index < end ; index++) {
//		if(dvma_entry_use(index))
//			BUG();
160 161
//		pr_info("mapping pa %lx to ba %lx\n", __pa(kaddr),
//			index << DVMA_PAGE_SHIFT);
Linus Torvalds's avatar
Linus Torvalds committed
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

		dvma_entry_set(index, __pa(kaddr));

		iommu_pte[index] |= IOMMU_FULL_BLOCK;
//		dvma_entry_inc(index);

		kaddr += DVMA_PAGE_SIZE;
	}

#ifdef DEBUG
	for(index = (baddr >> DVMA_PAGE_SHIFT); index < end; index++)
		dvma_print(index << DVMA_PAGE_SHIFT);
#endif
	return 0;

}

void dvma_unmap_iommu(unsigned long baddr, int len)
{

	int index, end;


	index = baddr >> DVMA_PAGE_SHIFT;
	end = (DVMA_PAGE_ALIGN(baddr+len) >> DVMA_PAGE_SHIFT);

	for(; index < end ; index++) {
189 190
		pr_debug("freeing bus mapping %08x\n",
			 index << DVMA_PAGE_SHIFT);
Linus Torvalds's avatar
Linus Torvalds committed
191 192
#if 0
		if(!dvma_entry_use(index))
193 194
			pr_info("dvma_unmap freeing unused entry %04x\n",
				index);
Linus Torvalds's avatar
Linus Torvalds committed
195 196 197 198 199 200 201
		else
			dvma_entry_dec(index);
#endif
		dvma_entry_clr(index);
	}

}