Home | History | Annotate | Download | only in arm32
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Copyright (C) 2012-2014 Panasonic Corporation
      4  * Copyright (C) 2015-2016 Socionext Inc.
      5  *   Author: Masahiro Yamada <yamada.masahiro (at) socionext.com>
      6  */
      7 
      8 #include <common.h>
      9 #include <linux/io.h>
     10 #include <linux/kernel.h>
     11 #include <asm/armv7.h>
     12 #include <asm/processor.h>
     13 
     14 #include "cache-uniphier.h"
     15 
     16 /* control registers */
     17 #define UNIPHIER_SSCC		0x500c0000	/* Control Register */
     18 #define    UNIPHIER_SSCC_BST			(0x1 << 20)	/* UCWG burst read */
     19 #define    UNIPHIER_SSCC_ACT			(0x1 << 19)	/* Inst-Data separate */
     20 #define    UNIPHIER_SSCC_WTG			(0x1 << 18)	/* WT gathering on */
     21 #define    UNIPHIER_SSCC_PRD			(0x1 << 17)	/* enable pre-fetch */
     22 #define    UNIPHIER_SSCC_ON			(0x1 <<  0)	/* enable cache */
     23 #define UNIPHIER_SSCLPDAWCR	0x500c0030	/* Unified/Data Active Way Control */
     24 #define UNIPHIER_SSCLPIAWCR	0x500c0034	/* Instruction Active Way Control */
     25 
     26 /* revision registers */
     27 #define UNIPHIER_SSCID		0x503c0100	/* ID Register */
     28 
     29 /* operation registers */
     30 #define UNIPHIER_SSCOPE		0x506c0244	/* Cache Operation Primitive Entry */
     31 #define    UNIPHIER_SSCOPE_CM_INV		0x0	/* invalidate */
     32 #define    UNIPHIER_SSCOPE_CM_CLEAN		0x1	/* clean */
     33 #define    UNIPHIER_SSCOPE_CM_FLUSH		0x2	/* flush */
     34 #define    UNIPHIER_SSCOPE_CM_SYNC		0x8	/* sync (drain bufs) */
     35 #define    UNIPHIER_SSCOPE_CM_FLUSH_PREFETCH	0x9	/* flush p-fetch buf */
     36 #define UNIPHIER_SSCOQM		0x506c0248
     37 #define    UNIPHIER_SSCOQM_TID_MASK		(0x3 << 21)
     38 #define    UNIPHIER_SSCOQM_TID_LRU_DATA		(0x0 << 21)
     39 #define    UNIPHIER_SSCOQM_TID_LRU_INST		(0x1 << 21)
     40 #define    UNIPHIER_SSCOQM_TID_WAY		(0x2 << 21)
     41 #define    UNIPHIER_SSCOQM_S_MASK		(0x3 << 17)
     42 #define    UNIPHIER_SSCOQM_S_RANGE		(0x0 << 17)
     43 #define    UNIPHIER_SSCOQM_S_ALL		(0x1 << 17)
     44 #define    UNIPHIER_SSCOQM_S_WAY		(0x2 << 17)
     45 #define    UNIPHIER_SSCOQM_CE			(0x1 << 15)	/* notify completion */
     46 #define    UNIPHIER_SSCOQM_CW			(0x1 << 14)
     47 #define    UNIPHIER_SSCOQM_CM_MASK		(0x7)
     48 #define    UNIPHIER_SSCOQM_CM_INV		0x0	/* invalidate */
     49 #define    UNIPHIER_SSCOQM_CM_CLEAN		0x1	/* clean */
     50 #define    UNIPHIER_SSCOQM_CM_FLUSH		0x2	/* flush */
     51 #define    UNIPHIER_SSCOQM_CM_PREFETCH		0x3	/* prefetch to cache */
     52 #define    UNIPHIER_SSCOQM_CM_PREFETCH_BUF	0x4	/* prefetch to pf-buf */
     53 #define    UNIPHIER_SSCOQM_CM_TOUCH		0x5	/* touch */
     54 #define    UNIPHIER_SSCOQM_CM_TOUCH_ZERO	0x6	/* touch to zero */
     55 #define    UNIPHIER_SSCOQM_CM_TOUCH_DIRTY	0x7	/* touch with dirty */
     56 #define UNIPHIER_SSCOQAD	0x506c024c	/* Cache Operation Queue Address */
     57 #define UNIPHIER_SSCOQSZ	0x506c0250	/* Cache Operation Queue Size */
     58 #define UNIPHIER_SSCOQMASK	0x506c0254	/* Cache Operation Queue Address Mask */
     59 #define UNIPHIER_SSCOQWN	0x506c0258	/* Cache Operation Queue Way Number */
     60 #define UNIPHIER_SSCOPPQSEF	0x506c025c	/* Cache Operation Queue Set Complete */
     61 #define    UNIPHIER_SSCOPPQSEF_FE		(0x1 << 1)
     62 #define    UNIPHIER_SSCOPPQSEF_OE		(0x1 << 0)
     63 #define UNIPHIER_SSCOLPQS	0x506c0260	/* Cache Operation Queue Status */
     64 #define    UNIPHIER_SSCOLPQS_EF			(0x1 << 2)
     65 #define    UNIPHIER_SSCOLPQS_EST		(0x1 << 1)
     66 #define    UNIPHIER_SSCOLPQS_QST		(0x1 << 0)
     67 
     68 #define UNIPHIER_SSC_LINE_SIZE		128
     69 #define UNIPHIER_SSC_RANGE_OP_MAX_SIZE	(0x00400000 - (UNIPHIER_SSC_LINE_SIZE))
     70 
     71 #define UNIPHIER_SSCOQAD_IS_NEEDED(op) \
     72 		((op & UNIPHIER_SSCOQM_S_MASK) == UNIPHIER_SSCOQM_S_RANGE)
     73 #define UNIPHIER_SSCOQWM_IS_NEEDED(op) \
     74 		(((op & UNIPHIER_SSCOQM_S_MASK) == UNIPHIER_SSCOQM_S_WAY) || \
     75 		 ((op & UNIPHIER_SSCOQM_TID_MASK) == UNIPHIER_SSCOQM_TID_WAY))
     76 
     77 /* uniphier_cache_sync - perform a sync point for a particular cache level */
     78 static void uniphier_cache_sync(void)
     79 {
     80 	/* drain internal buffers */
     81 	writel(UNIPHIER_SSCOPE_CM_SYNC, UNIPHIER_SSCOPE);
     82 	/* need a read back to confirm */
     83 	readl(UNIPHIER_SSCOPE);
     84 }
     85 
     86 /**
     87  * uniphier_cache_maint_common - run a queue operation
     88  *
     89  * @start: start address of range operation (don't care for "all" operation)
     90  * @size: data size of range operation (don't care for "all" operation)
     91  * @ways: target ways (don't care for operations other than pre-fetch, touch
     92  * @operation: flags to specify the desired cache operation
     93  */
     94 static void uniphier_cache_maint_common(u32 start, u32 size, u32 ways,
     95 					u32 operation)
     96 {
     97 	/* clear the complete notification flag */
     98 	writel(UNIPHIER_SSCOLPQS_EF, UNIPHIER_SSCOLPQS);
     99 
    100 	do {
    101 		/* set cache operation */
    102 		writel(UNIPHIER_SSCOQM_CE | operation, UNIPHIER_SSCOQM);
    103 
    104 		/* set address range if needed */
    105 		if (likely(UNIPHIER_SSCOQAD_IS_NEEDED(operation))) {
    106 			writel(start, UNIPHIER_SSCOQAD);
    107 			writel(size, UNIPHIER_SSCOQSZ);
    108 		}
    109 
    110 		/* set target ways if needed */
    111 		if (unlikely(UNIPHIER_SSCOQWM_IS_NEEDED(operation)))
    112 			writel(ways, UNIPHIER_SSCOQWN);
    113 	} while (unlikely(readl(UNIPHIER_SSCOPPQSEF) &
    114 			  (UNIPHIER_SSCOPPQSEF_FE | UNIPHIER_SSCOPPQSEF_OE)));
    115 
    116 	/* wait until the operation is completed */
    117 	while (likely(readl(UNIPHIER_SSCOLPQS) != UNIPHIER_SSCOLPQS_EF))
    118 		cpu_relax();
    119 }
    120 
    121 static void uniphier_cache_maint_all(u32 operation)
    122 {
    123 	uniphier_cache_maint_common(0, 0, 0, UNIPHIER_SSCOQM_S_ALL | operation);
    124 
    125 	uniphier_cache_sync();
    126 }
    127 
    128 static void uniphier_cache_maint_range(u32 start, u32 end, u32 ways,
    129 				       u32 operation)
    130 {
    131 	u32 size;
    132 
    133 	/*
    134 	 * If the start address is not aligned,
    135 	 * perform a cache operation for the first cache-line
    136 	 */
    137 	start = start & ~(UNIPHIER_SSC_LINE_SIZE - 1);
    138 
    139 	size = end - start;
    140 
    141 	if (unlikely(size >= (u32)(-UNIPHIER_SSC_LINE_SIZE))) {
    142 		/* this means cache operation for all range */
    143 		uniphier_cache_maint_all(operation);
    144 		return;
    145 	}
    146 
    147 	/*
    148 	 * If the end address is not aligned,
    149 	 * perform a cache operation for the last cache-line
    150 	 */
    151 	size = ALIGN(size, UNIPHIER_SSC_LINE_SIZE);
    152 
    153 	while (size) {
    154 		u32 chunk_size = min_t(u32, size, UNIPHIER_SSC_RANGE_OP_MAX_SIZE);
    155 
    156 		uniphier_cache_maint_common(start, chunk_size, ways,
    157 					    UNIPHIER_SSCOQM_S_RANGE | operation);
    158 
    159 		start += chunk_size;
    160 		size -= chunk_size;
    161 	}
    162 
    163 	uniphier_cache_sync();
    164 }
    165 
    166 void uniphier_cache_prefetch_range(u32 start, u32 end, u32 ways)
    167 {
    168 	uniphier_cache_maint_range(start, end, ways,
    169 				   UNIPHIER_SSCOQM_TID_WAY |
    170 				   UNIPHIER_SSCOQM_CM_PREFETCH);
    171 }
    172 
    173 void uniphier_cache_touch_range(u32 start, u32 end, u32 ways)
    174 {
    175 	uniphier_cache_maint_range(start, end, ways,
    176 				   UNIPHIER_SSCOQM_TID_WAY |
    177 				   UNIPHIER_SSCOQM_CM_TOUCH);
    178 }
    179 
    180 void uniphier_cache_touch_zero_range(u32 start, u32 end, u32 ways)
    181 {
    182 	uniphier_cache_maint_range(start, end, ways,
    183 				   UNIPHIER_SSCOQM_TID_WAY |
    184 				   UNIPHIER_SSCOQM_CM_TOUCH_ZERO);
    185 }
    186 
    187 void uniphier_cache_inv_way(u32 ways)
    188 {
    189 	uniphier_cache_maint_common(0, 0, ways,
    190 				    UNIPHIER_SSCOQM_S_WAY |
    191 				    UNIPHIER_SSCOQM_CM_INV);
    192 }
    193 
    194 void uniphier_cache_set_active_ways(int cpu, u32 active_ways)
    195 {
    196 	void __iomem *base = (void __iomem *)UNIPHIER_SSCC + 0xc00;
    197 
    198 	switch (readl(UNIPHIER_SSCID)) { /* revision */
    199 	case 0x12:	/* LD4 */
    200 	case 0x16:	/* sld8 */
    201 		base = (void __iomem *)UNIPHIER_SSCC + 0x840;
    202 		break;
    203 	default:
    204 		base = (void __iomem *)UNIPHIER_SSCC + 0xc00;
    205 		break;
    206 	}
    207 
    208 	writel(active_ways, base + 4 * cpu);
    209 }
    210 
    211 static void uniphier_cache_endisable(int enable)
    212 {
    213 	u32 tmp;
    214 
    215 	tmp = readl(UNIPHIER_SSCC);
    216 	if (enable)
    217 		tmp |= UNIPHIER_SSCC_ON;
    218 	else
    219 		tmp &= ~UNIPHIER_SSCC_ON;
    220 	writel(tmp, UNIPHIER_SSCC);
    221 }
    222 
    223 void uniphier_cache_enable(void)
    224 {
    225 	uniphier_cache_endisable(1);
    226 }
    227 
    228 void uniphier_cache_disable(void)
    229 {
    230 	uniphier_cache_endisable(0);
    231 }
    232 
    233 #ifdef CONFIG_CACHE_UNIPHIER
    234 void v7_outer_cache_flush_all(void)
    235 {
    236 	uniphier_cache_maint_all(UNIPHIER_SSCOQM_CM_FLUSH);
    237 }
    238 
    239 void v7_outer_cache_inval_all(void)
    240 {
    241 	uniphier_cache_maint_all(UNIPHIER_SSCOQM_CM_INV);
    242 }
    243 
    244 void v7_outer_cache_flush_range(u32 start, u32 end)
    245 {
    246 	uniphier_cache_maint_range(start, end, 0, UNIPHIER_SSCOQM_CM_FLUSH);
    247 }
    248 
    249 void v7_outer_cache_inval_range(u32 start, u32 end)
    250 {
    251 	if (start & (UNIPHIER_SSC_LINE_SIZE - 1)) {
    252 		start &= ~(UNIPHIER_SSC_LINE_SIZE - 1);
    253 		uniphier_cache_maint_range(start, UNIPHIER_SSC_LINE_SIZE, 0,
    254 					   UNIPHIER_SSCOQM_CM_FLUSH);
    255 		start += UNIPHIER_SSC_LINE_SIZE;
    256 	}
    257 
    258 	if (start >= end) {
    259 		uniphier_cache_sync();
    260 		return;
    261 	}
    262 
    263 	if (end & (UNIPHIER_SSC_LINE_SIZE - 1)) {
    264 		end &= ~(UNIPHIER_SSC_LINE_SIZE - 1);
    265 		uniphier_cache_maint_range(end, UNIPHIER_SSC_LINE_SIZE, 0,
    266 					   UNIPHIER_SSCOQM_CM_FLUSH);
    267 	}
    268 
    269 	if (start >= end) {
    270 		uniphier_cache_sync();
    271 		return;
    272 	}
    273 
    274 	uniphier_cache_maint_range(start, end, 0, UNIPHIER_SSCOQM_CM_INV);
    275 }
    276 
    277 void v7_outer_cache_enable(void)
    278 {
    279 	uniphier_cache_set_active_ways(0, U32_MAX);	/* activate all ways */
    280 	uniphier_cache_enable();
    281 }
    282 
    283 void v7_outer_cache_disable(void)
    284 {
    285 	uniphier_cache_disable();
    286 }
    287 #endif
    288 
    289 void enable_caches(void)
    290 {
    291 	dcache_enable();
    292 }
    293