summaryrefslogtreecommitdiff
path: root/arch/csky/mm/cachev1.c
blob: 5a5a9804a0e3d468baa2ff7444d91265d35f9783 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.

#include <linux/spinlock.h>
#include <asm/cache.h>
#include <abi/reg_ops.h>

/* for L1-cache */
#define INS_CACHE		(1 << 0)
#define DATA_CACHE		(1 << 1)
#define CACHE_INV		(1 << 4)
#define CACHE_CLR		(1 << 5)
#define CACHE_OMS		(1 << 6)
#define CACHE_ITS		(1 << 7)
#define CACHE_LICF		(1 << 31)

/* for L2-cache */
#define CR22_LEVEL_SHIFT	(1)
#define CR22_SET_SHIFT		(7)
#define CR22_WAY_SHIFT		(30)
#define CR22_WAY_SHIFT_L2	(29)

static DEFINE_SPINLOCK(cache_lock);

static inline void cache_op_line(unsigned long i, unsigned int val)
{
	mtcr("cr22", i);
	mtcr("cr17", val);
}

#define CCR2_L2E (1 << 3)
static void cache_op_all(unsigned int value, unsigned int l2)
{
	mtcr("cr17", value | CACHE_CLR);
	mb();

	if (l2 && (mfcr_ccr2() & CCR2_L2E)) {
		mtcr("cr24", value | CACHE_CLR);
		mb();
	}
}

static void cache_op_range(
	unsigned int start,
	unsigned int end,
	unsigned int value,
	unsigned int l2)
{
	unsigned long i, flags;
	unsigned int val = value | CACHE_CLR | CACHE_OMS;
	bool l2_sync;

	if (unlikely((end - start) >= PAGE_SIZE) ||
	    unlikely(start < PAGE_OFFSET) ||
	    unlikely(start >= PAGE_OFFSET + LOWMEM_LIMIT)) {
		cache_op_all(value, l2);
		return;
	}

	if ((mfcr_ccr2() & CCR2_L2E) && l2)
		l2_sync = 1;
	else
		l2_sync = 0;

	spin_lock_irqsave(&cache_lock, flags);

	i = start & ~(L1_CACHE_BYTES - 1);
	for (; i < end; i += L1_CACHE_BYTES) {
		cache_op_line(i, val);
		if (l2_sync) {
			mb();
			mtcr("cr24", val);
		}
	}
	spin_unlock_irqrestore(&cache_lock, flags);

	mb();
}

void dcache_wb_line(unsigned long start)
{
	asm volatile("idly4\n":::"memory");
	cache_op_line(start, DATA_CACHE|CACHE_CLR);
	mb();
}

void icache_inv_range(unsigned long start, unsigned long end)
{
	cache_op_range(start, end, INS_CACHE|CACHE_INV, 0);
}

void icache_inv_all(void)
{
	cache_op_all(INS_CACHE|CACHE_INV, 0);
}

void local_icache_inv_all(void *priv)
{
	cache_op_all(INS_CACHE|CACHE_INV, 0);
}

void dcache_wb_range(unsigned long start, unsigned long end)
{
	cache_op_range(start, end, DATA_CACHE|CACHE_CLR, 0);
}

void dcache_wbinv_all(void)
{
	cache_op_all(DATA_CACHE|CACHE_CLR|CACHE_INV, 0);
}

void cache_wbinv_range(unsigned long start, unsigned long end)
{
	cache_op_range(start, end, INS_CACHE|DATA_CACHE|CACHE_CLR|CACHE_INV, 0);
}
EXPORT_SYMBOL(cache_wbinv_range);

void cache_wbinv_all(void)
{
	cache_op_all(INS_CACHE|DATA_CACHE|CACHE_CLR|CACHE_INV, 0);
}

void dma_wbinv_range(unsigned long start, unsigned long end)
{
	cache_op_range(start, end, DATA_CACHE|CACHE_CLR|CACHE_INV, 1);
}

void dma_inv_range(unsigned long start, unsigned long end)
{
	cache_op_range(start, end, DATA_CACHE|CACHE_CLR|CACHE_INV, 1);
}

void dma_wb_range(unsigned long start, unsigned long end)
{
	cache_op_range(start, end, DATA_CACHE|CACHE_CLR|CACHE_INV, 1);
}