summaryrefslogtreecommitdiff
path: root/drivers/cache/starfive_starlink_cache.c
blob: 24c7d078ca2272851866dec68930d5c978d39eda (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
// SPDX-License-Identifier: GPL-2.0
/*
 * Cache Management Operations for StarFive's Starlink cache controller
 *
 * Copyright (C) 2024 Shanghai StarFive Technology Co., Ltd.
 *
 * Author: Joshua Yeong <joshua.yeong@starfivetech.com>
 */

#include <linux/bitfield.h>
#include <linux/cacheflush.h>
#include <linux/iopoll.h>
#include <linux/of_address.h>

#include <asm/dma-noncoherent.h>

#define STARLINK_CACHE_FLUSH_START_ADDR			0x0
#define STARLINK_CACHE_FLUSH_END_ADDR			0x8
#define STARLINK_CACHE_FLUSH_CTL			0x10
#define STARLINK_CACHE_ALIGN				0x40

#define STARLINK_CACHE_ADDRESS_RANGE_MASK		GENMASK(39, 0)
#define STARLINK_CACHE_FLUSH_CTL_MODE_MASK		GENMASK(2, 1)
#define STARLINK_CACHE_FLUSH_CTL_ENABLE_MASK		BIT(0)

#define STARLINK_CACHE_FLUSH_CTL_CLEAN_INVALIDATE	0
#define STARLINK_CACHE_FLUSH_CTL_MAKE_INVALIDATE	1
#define STARLINK_CACHE_FLUSH_CTL_CLEAN_SHARED		2
#define STARLINK_CACHE_FLUSH_POLL_DELAY_US		1
#define STARLINK_CACHE_FLUSH_TIMEOUT_US			5000000

static void __iomem *starlink_cache_base;

static void starlink_cache_flush_complete(void)
{
	volatile void __iomem *ctl = starlink_cache_base + STARLINK_CACHE_FLUSH_CTL;
	u64 v;
	int ret;

	ret = readq_poll_timeout_atomic(ctl, v, !(v & STARLINK_CACHE_FLUSH_CTL_ENABLE_MASK),
					STARLINK_CACHE_FLUSH_POLL_DELAY_US,
					STARLINK_CACHE_FLUSH_TIMEOUT_US);
	if (ret)
		WARN(1, "StarFive Starlink cache flush operation timeout\n");
}

static void starlink_cache_dma_cache_wback(phys_addr_t paddr, unsigned long size)
{
	writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr),
	       starlink_cache_base + STARLINK_CACHE_FLUSH_START_ADDR);
	writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr + size),
	       starlink_cache_base + STARLINK_CACHE_FLUSH_END_ADDR);

	mb();
	writeq(FIELD_PREP(STARLINK_CACHE_FLUSH_CTL_MODE_MASK,
			  STARLINK_CACHE_FLUSH_CTL_CLEAN_SHARED),
	       starlink_cache_base + STARLINK_CACHE_FLUSH_CTL);

	starlink_cache_flush_complete();
}

static void starlink_cache_dma_cache_invalidate(phys_addr_t paddr, unsigned long size)
{
	writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr),
	       starlink_cache_base + STARLINK_CACHE_FLUSH_START_ADDR);
	writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr + size),
	       starlink_cache_base + STARLINK_CACHE_FLUSH_END_ADDR);

	mb();
	writeq(FIELD_PREP(STARLINK_CACHE_FLUSH_CTL_MODE_MASK,
			  STARLINK_CACHE_FLUSH_CTL_MAKE_INVALIDATE),
	       starlink_cache_base + STARLINK_CACHE_FLUSH_CTL);

	starlink_cache_flush_complete();
}

static void starlink_cache_dma_cache_wback_inv(phys_addr_t paddr, unsigned long size)
{
	writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr),
	       starlink_cache_base + STARLINK_CACHE_FLUSH_START_ADDR);
	writeq(FIELD_PREP(STARLINK_CACHE_ADDRESS_RANGE_MASK, paddr + size),
	       starlink_cache_base + STARLINK_CACHE_FLUSH_END_ADDR);

	mb();
	writeq(FIELD_PREP(STARLINK_CACHE_FLUSH_CTL_MODE_MASK,
			  STARLINK_CACHE_FLUSH_CTL_CLEAN_INVALIDATE),
	       starlink_cache_base + STARLINK_CACHE_FLUSH_CTL);

	starlink_cache_flush_complete();
}

static const struct riscv_nonstd_cache_ops starlink_cache_ops = {
	.wback = &starlink_cache_dma_cache_wback,
	.inv = &starlink_cache_dma_cache_invalidate,
	.wback_inv = &starlink_cache_dma_cache_wback_inv,
};

static const struct of_device_id starlink_cache_ids[] = {
	{ .compatible = "starfive,jh8100-starlink-cache" },
	{ /* sentinel */ }
};

static int __init starlink_cache_init(void)
{
	struct device_node *np;
	u32 block_size;
	int ret;

	np = of_find_matching_node(NULL, starlink_cache_ids);
	if (!of_device_is_available(np))
		return -ENODEV;

	ret = of_property_read_u32(np, "cache-block-size", &block_size);
	if (ret)
		return ret;

	if (block_size % STARLINK_CACHE_ALIGN)
		return -EINVAL;

	starlink_cache_base = of_iomap(np, 0);
	if (!starlink_cache_base)
		return -ENOMEM;

	riscv_cbom_block_size = block_size;
	riscv_noncoherent_supported();
	riscv_noncoherent_register_cache_ops(&starlink_cache_ops);

	return 0;
}
arch_initcall(starlink_cache_init);