Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/openrisc/kernel/dma.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* OpenRISC Linux
4
*
5
* Linux architectural port borrowing liberally from similar works of
6
* others. All original copyrights apply as per the original source
7
* declaration.
8
*
9
* Modifications for the OpenRISC architecture:
10
* Copyright (C) 2003 Matjaz Breskvar <[email protected]>
11
* Copyright (C) 2010-2011 Jonas Bonn <[email protected]>
12
*
13
* DMA mapping callbacks...
14
*/
15
16
#include <linux/dma-map-ops.h>
17
#include <linux/pagewalk.h>
18
19
#include <asm/cpuinfo.h>
20
#include <asm/cacheflush.h>
21
#include <asm/spr_defs.h>
22
#include <asm/tlbflush.h>
23
24
static int
25
page_set_nocache(pte_t *pte, unsigned long addr,
26
unsigned long next, struct mm_walk *walk)
27
{
28
pte_val(*pte) |= _PAGE_CI;
29
30
/*
31
* Flush the page out of the TLB so that the new page flags get
32
* picked up next time there's an access
33
*/
34
flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
35
36
/* Flush page out of dcache */
37
local_dcache_range_flush(__pa(addr), __pa(next));
38
39
return 0;
40
}
41
42
static const struct mm_walk_ops set_nocache_walk_ops = {
43
.pte_entry = page_set_nocache,
44
};
45
46
static int
47
page_clear_nocache(pte_t *pte, unsigned long addr,
48
unsigned long next, struct mm_walk *walk)
49
{
50
pte_val(*pte) &= ~_PAGE_CI;
51
52
/*
53
* Flush the page out of the TLB so that the new page flags get
54
* picked up next time there's an access
55
*/
56
flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
57
58
return 0;
59
}
60
61
static const struct mm_walk_ops clear_nocache_walk_ops = {
62
.pte_entry = page_clear_nocache,
63
};
64
65
void *arch_dma_set_uncached(void *cpu_addr, size_t size)
66
{
67
unsigned long va = (unsigned long)cpu_addr;
68
int error;
69
70
/*
71
* We need to iterate through the pages, clearing the dcache for
72
* them and setting the cache-inhibit bit.
73
*/
74
mmap_write_lock(&init_mm);
75
error = walk_kernel_page_table_range(va, va + size,
76
&set_nocache_walk_ops, NULL, NULL);
77
mmap_write_unlock(&init_mm);
78
79
if (error)
80
return ERR_PTR(error);
81
return cpu_addr;
82
}
83
84
void arch_dma_clear_uncached(void *cpu_addr, size_t size)
85
{
86
unsigned long va = (unsigned long)cpu_addr;
87
88
mmap_write_lock(&init_mm);
89
/* walk_page_range shouldn't be able to fail here */
90
WARN_ON(walk_kernel_page_table_range(va, va + size,
91
&clear_nocache_walk_ops, NULL, NULL));
92
mmap_write_unlock(&init_mm);
93
}
94
95
void arch_sync_dma_for_device(phys_addr_t addr, size_t size,
96
enum dma_data_direction dir)
97
{
98
switch (dir) {
99
case DMA_TO_DEVICE:
100
/* Flush the dcache for the requested range */
101
local_dcache_range_flush(addr, addr + size);
102
break;
103
case DMA_FROM_DEVICE:
104
/* Invalidate the dcache for the requested range */
105
local_dcache_range_inv(addr, addr + size);
106
break;
107
default:
108
/*
109
* NOTE: If dir == DMA_BIDIRECTIONAL then there's no need to
110
* flush nor invalidate the cache here as the area will need
111
* to be manually synced anyway.
112
*/
113
break;
114
}
115
}
116
117