summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/mach-mpc85xx/fsl_law.c
blob: 2254c925447645653647ae0a20708125cd804964 (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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 * Copyright 2012 GE Intelligent Platforms, Inc.
 *
 * Copyright 2008-2011 Freescale Semiconductor, Inc.
 *
 * (C) Copyright 2000
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <common.h>
#include <asm/config.h>
#include <asm/fsl_law.h>
#include <mach/ffs64.h>

#define FSL_HW_NUM_LAWS FSL_NUM_LAWS

#define LAW_BASE (CFG_IMMR + 0xc08)
#define LAWAR_ADDR(x) ((u32 *)LAW_BASE + 8 * (x) + 2)
#define LAWBAR_ADDR(x) ((u32 *)LAW_BASE + 8 * (x))
#define LAWBAR_SHIFT 12

static inline phys_addr_t fsl_get_law_base_addr(int idx)
{
	return (phys_addr_t)in_be32(LAWBAR_ADDR(idx)) << LAWBAR_SHIFT;
}

static inline void fsl_set_law_base_addr(int idx, phys_addr_t addr)
{
	out_be32(LAWBAR_ADDR(idx), addr >> LAWBAR_SHIFT);
}

static void fsl_set_law(u8 idx, phys_addr_t addr, enum law_size sz,
		    enum law_trgt_if id)
{
	out_be32(LAWAR_ADDR(idx), 0);
	fsl_set_law_base_addr(idx, addr);
	out_be32(LAWAR_ADDR(idx), LAW_EN | ((u32)id << 20) | (u32)sz);

	/* Read back so that we sync the writes */
	in_be32(LAWAR_ADDR(idx));
}

static int fsl_is_free_law(int idx)
{
	u32 lawar;

	lawar = in_be32(LAWAR_ADDR(idx));
	if (!(lawar & LAW_EN))
		return 1;

	return 0;
}

static void fsl_set_next_law(phys_addr_t addr, enum law_size sz,
			enum law_trgt_if id)
{
	u32 idx;

	for (idx = 0; idx < FSL_HW_NUM_LAWS; idx++) {
		if (fsl_is_free_law(idx)) {
			fsl_set_law(idx, addr, sz, id);
			break;
		}
	}

	if (idx >= FSL_HW_NUM_LAWS)
		panic("No more LAWS available\n");
}

static void fsl_set_last_law(phys_addr_t addr, enum law_size sz,
			enum law_trgt_if id)
{
	u32 idx;

	for (idx = (FSL_HW_NUM_LAWS - 1); idx >= 0; idx--) {
		if (fsl_is_free_law(idx)) {
			fsl_set_law(idx, addr, sz, id);
			break;
		}
	}

	if (idx < 0)
		panic("No more LAWS available\n");
}

/* use up to 2 LAWs for DDR, use the last available LAWs */
int fsl_set_ddr_laws(u64 start, u64 sz, enum law_trgt_if id)
{
	u64 start_align, law_sz;
	int law_sz_enc;

	if (start == 0)
		start_align = 1ull << (LAW_SIZE_32G + 1);
	else
		start_align = 1ull << (ffs64(start) - 1);

	law_sz = min(start_align, sz);
	law_sz_enc = __ilog2_u64(law_sz) - 1;

	fsl_set_last_law(start, law_sz_enc, id);

	/* recalculate size based on what was actually covered by the law */
	law_sz = 1ull << __ilog2_u64(law_sz);

	/* do we still have anything to map */
	sz = sz - law_sz;
	if (sz) {
		start += law_sz;

		start_align = 1ull << (ffs64(start) - 1);
		law_sz = min(start_align, sz);
		law_sz_enc = __ilog2_u64(law_sz) - 1;

		fsl_set_last_law(start, law_sz_enc, id);
	} else {
		return 0;
	}

	/* do we still have anything to map */
	sz = sz - law_sz;
	if (sz)
		return 1;

	return 0;
}

void fsl_init_laws(void)
{
	int i;

	if (FSL_HW_NUM_LAWS > 32)
		panic("FSL_HW_NUM_LAWS can not be > 32 w/o code changes");

	for (i = 0; i < num_law_entries; i++) {
		if (law_table[i].index == -1)
			fsl_set_next_law(law_table[i].addr,
					law_table[i].size,
					law_table[i].trgt_id);
		else
			fsl_set_law(law_table[i].index, law_table[i].addr,
				law_table[i].size, law_table[i].trgt_id);
	}
}