blob: ec85dda1e73385b90fdfdab9840ef9136d657f39 [file] [log] [blame]
Catalin Marinas382266a2007-02-05 14:48:19 +01001/*
2 * arch/arm/mm/cache-l2x0.c - L210/L220 cache controller support
3 *
4 * Copyright (C) 2007 ARM Limited
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <linux/init.h>
Catalin Marinas07620972007-07-20 11:42:40 +010020#include <linux/spinlock.h>
Russell Kingfced80c2008-09-06 12:10:45 +010021#include <linux/io.h>
Catalin Marinas382266a2007-02-05 14:48:19 +010022
23#include <asm/cacheflush.h>
Catalin Marinas382266a2007-02-05 14:48:19 +010024#include <asm/hardware/cache-l2x0.h>
25
26#define CACHE_LINE_SIZE 32
27
28static void __iomem *l2x0_base;
Catalin Marinas07620972007-07-20 11:42:40 +010029static DEFINE_SPINLOCK(l2x0_lock);
Catalin Marinas382266a2007-02-05 14:48:19 +010030
Russell King3d107432009-11-19 11:41:09 +000031static inline void cache_wait(void __iomem *reg, unsigned long mask)
Catalin Marinas382266a2007-02-05 14:48:19 +010032{
Catalin Marinas382266a2007-02-05 14:48:19 +010033 /* wait for the operation to complete */
Russell King3d107432009-11-19 11:41:09 +000034 while (readl(reg) & mask)
Catalin Marinas382266a2007-02-05 14:48:19 +010035 ;
Catalin Marinas382266a2007-02-05 14:48:19 +010036}
37
38static inline void cache_sync(void)
39{
Russell King3d107432009-11-19 11:41:09 +000040 void __iomem *base = l2x0_base;
41 writel(0, base + L2X0_CACHE_SYNC);
42 cache_wait(base + L2X0_CACHE_SYNC, 1);
Catalin Marinas382266a2007-02-05 14:48:19 +010043}
44
45static inline void l2x0_inv_all(void)
46{
Russell King0eb948d2009-11-19 11:12:15 +000047 unsigned long flags;
48
Catalin Marinas382266a2007-02-05 14:48:19 +010049 /* invalidate all ways */
Russell King0eb948d2009-11-19 11:12:15 +000050 spin_lock_irqsave(&l2x0_lock, flags);
Russell King3d107432009-11-19 11:41:09 +000051 writel(0xff, l2x0_base + L2X0_INV_WAY);
52 cache_wait(l2x0_base + L2X0_INV_WAY, 0xff);
Catalin Marinas382266a2007-02-05 14:48:19 +010053 cache_sync();
Russell King0eb948d2009-11-19 11:12:15 +000054 spin_unlock_irqrestore(&l2x0_lock, flags);
Catalin Marinas382266a2007-02-05 14:48:19 +010055}
56
57static void l2x0_inv_range(unsigned long start, unsigned long end)
58{
Russell King3d107432009-11-19 11:41:09 +000059 void __iomem *base = l2x0_base;
Russell King0eb948d2009-11-19 11:12:15 +000060 unsigned long flags;
Catalin Marinas382266a2007-02-05 14:48:19 +010061
Russell King0eb948d2009-11-19 11:12:15 +000062 spin_lock_irqsave(&l2x0_lock, flags);
Rui Sousa4f6627a2007-09-15 00:56:19 +010063 if (start & (CACHE_LINE_SIZE - 1)) {
64 start &= ~(CACHE_LINE_SIZE - 1);
Russell King3d107432009-11-19 11:41:09 +000065 cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
66 writel(start, base + L2X0_CLEAN_INV_LINE_PA);
Rui Sousa4f6627a2007-09-15 00:56:19 +010067 start += CACHE_LINE_SIZE;
68 }
69
70 if (end & (CACHE_LINE_SIZE - 1)) {
71 end &= ~(CACHE_LINE_SIZE - 1);
Russell King3d107432009-11-19 11:41:09 +000072 cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
73 writel(end, base + L2X0_CLEAN_INV_LINE_PA);
Rui Sousa4f6627a2007-09-15 00:56:19 +010074 }
75
Russell King0eb948d2009-11-19 11:12:15 +000076 while (start < end) {
77 unsigned long blk_end = start + min(end - start, 4096UL);
78
79 while (start < blk_end) {
Russell King3d107432009-11-19 11:41:09 +000080 cache_wait(base + L2X0_INV_LINE_PA, 1);
81 writel(start, base + L2X0_INV_LINE_PA);
Russell King0eb948d2009-11-19 11:12:15 +000082 start += CACHE_LINE_SIZE;
83 }
84
85 if (blk_end < end) {
86 spin_unlock_irqrestore(&l2x0_lock, flags);
87 spin_lock_irqsave(&l2x0_lock, flags);
88 }
89 }
Russell King3d107432009-11-19 11:41:09 +000090 cache_wait(base + L2X0_INV_LINE_PA, 1);
Catalin Marinas382266a2007-02-05 14:48:19 +010091 cache_sync();
Russell King0eb948d2009-11-19 11:12:15 +000092 spin_unlock_irqrestore(&l2x0_lock, flags);
Catalin Marinas382266a2007-02-05 14:48:19 +010093}
94
95static void l2x0_clean_range(unsigned long start, unsigned long end)
96{
Russell King3d107432009-11-19 11:41:09 +000097 void __iomem *base = l2x0_base;
Russell King0eb948d2009-11-19 11:12:15 +000098 unsigned long flags;
Catalin Marinas382266a2007-02-05 14:48:19 +010099
Russell King0eb948d2009-11-19 11:12:15 +0000100 spin_lock_irqsave(&l2x0_lock, flags);
Catalin Marinas382266a2007-02-05 14:48:19 +0100101 start &= ~(CACHE_LINE_SIZE - 1);
Russell King0eb948d2009-11-19 11:12:15 +0000102 while (start < end) {
103 unsigned long blk_end = start + min(end - start, 4096UL);
104
105 while (start < blk_end) {
Russell King3d107432009-11-19 11:41:09 +0000106 cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
107 writel(start, base + L2X0_CLEAN_LINE_PA);
Russell King0eb948d2009-11-19 11:12:15 +0000108 start += CACHE_LINE_SIZE;
109 }
110
111 if (blk_end < end) {
112 spin_unlock_irqrestore(&l2x0_lock, flags);
113 spin_lock_irqsave(&l2x0_lock, flags);
114 }
115 }
Russell King3d107432009-11-19 11:41:09 +0000116 cache_wait(base + L2X0_CLEAN_LINE_PA, 1);
Catalin Marinas382266a2007-02-05 14:48:19 +0100117 cache_sync();
Russell King0eb948d2009-11-19 11:12:15 +0000118 spin_unlock_irqrestore(&l2x0_lock, flags);
Catalin Marinas382266a2007-02-05 14:48:19 +0100119}
120
121static void l2x0_flush_range(unsigned long start, unsigned long end)
122{
Russell King3d107432009-11-19 11:41:09 +0000123 void __iomem *base = l2x0_base;
Russell King0eb948d2009-11-19 11:12:15 +0000124 unsigned long flags;
Catalin Marinas382266a2007-02-05 14:48:19 +0100125
Russell King0eb948d2009-11-19 11:12:15 +0000126 spin_lock_irqsave(&l2x0_lock, flags);
Catalin Marinas382266a2007-02-05 14:48:19 +0100127 start &= ~(CACHE_LINE_SIZE - 1);
Russell King0eb948d2009-11-19 11:12:15 +0000128 while (start < end) {
129 unsigned long blk_end = start + min(end - start, 4096UL);
130
131 while (start < blk_end) {
Russell King3d107432009-11-19 11:41:09 +0000132 cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
133 writel(start, base + L2X0_CLEAN_INV_LINE_PA);
Russell King0eb948d2009-11-19 11:12:15 +0000134 start += CACHE_LINE_SIZE;
135 }
136
137 if (blk_end < end) {
138 spin_unlock_irqrestore(&l2x0_lock, flags);
139 spin_lock_irqsave(&l2x0_lock, flags);
140 }
141 }
Russell King3d107432009-11-19 11:41:09 +0000142 cache_wait(base + L2X0_CLEAN_INV_LINE_PA, 1);
Catalin Marinas382266a2007-02-05 14:48:19 +0100143 cache_sync();
Russell King0eb948d2009-11-19 11:12:15 +0000144 spin_unlock_irqrestore(&l2x0_lock, flags);
Catalin Marinas382266a2007-02-05 14:48:19 +0100145}
146
147void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask)
148{
149 __u32 aux;
150
151 l2x0_base = base;
152
153 /* disable L2X0 */
154 writel(0, l2x0_base + L2X0_CTRL);
155
156 aux = readl(l2x0_base + L2X0_AUX_CTRL);
157 aux &= aux_mask;
158 aux |= aux_val;
159 writel(aux, l2x0_base + L2X0_AUX_CTRL);
160
161 l2x0_inv_all();
162
163 /* enable L2X0 */
164 writel(1, l2x0_base + L2X0_CTRL);
165
166 outer_cache.inv_range = l2x0_inv_range;
167 outer_cache.clean_range = l2x0_clean_range;
168 outer_cache.flush_range = l2x0_flush_range;
169
170 printk(KERN_INFO "L2X0 cache controller enabled\n");
171}