u-boot/drivers/misc/fsl_law.c
Kumar Gala 418ec85843 ppc/p4080: Add support for CoreNet style platform LAWs
On CoreNet based platforms the LAW address is split between an high &
low register and we no longer shift the address.  Also, the target IDs
on CoreNet platforms have been completely re-assigned.

Additionally, added a new find_law() API to which LAW an address hits in.
This is need for the CoreNet style boot release code since it will need
to determine what the target ID should be set to for boot window
translation.

Finally, enamed LAWAR_EN to LAW_EN and moved to header so we can use
it elsewhere.

Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
2009-09-24 12:05:28 -05:00

292 lines
6.7 KiB
C

/*
* Copyright 2008-2009 Freescale Semiconductor, Inc.
*
* (C) Copyright 2000
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <asm/fsl_law.h>
#include <asm/io.h>
DECLARE_GLOBAL_DATA_PTR;
/* number of LAWs in the hw implementation */
#if defined(CONFIG_MPC8540) || defined(CONFIG_MPC8541) || \
defined(CONFIG_MPC8560) || defined(CONFIG_MPC8555)
#define FSL_HW_NUM_LAWS 8
#elif defined(CONFIG_MPC8548) || defined(CONFIG_MPC8544) || \
defined(CONFIG_MPC8568) || defined(CONFIG_MPC8569) || \
defined(CONFIG_MPC8641) || defined(CONFIG_MPC8610)
#define FSL_HW_NUM_LAWS 10
#elif defined(CONFIG_MPC8536) || defined(CONFIG_MPC8572) || \
defined(CONFIG_P1011) || defined(CONFIG_P1020) || \
defined(CONFIG_P2010) || defined(CONFIG_P2020)
#define FSL_HW_NUM_LAWS 12
#else
#error FSL_HW_NUM_LAWS not defined for this platform
#endif
#ifdef CONFIG_FSL_CORENET
void set_law(u8 idx, phys_addr_t addr, enum law_size sz, enum law_trgt_if id)
{
volatile ccsr_local_t *ccm = (void *)(CONFIG_SYS_FSL_CORENET_CCM_ADDR);
gd->used_laws |= (1 << idx);
out_be32(&ccm->law[idx].lawar, 0);
out_be32(&ccm->law[idx].lawbarh, ((u64)addr >> 32));
out_be32(&ccm->law[idx].lawbarl, addr & 0xffffffff);
out_be32(&ccm->law[idx].lawar, LAW_EN | ((u32)id << 20) | (u32)sz);
/* Read back so that we sync the writes */
in_be32(&ccm->law[idx].lawar);
}
void disable_law(u8 idx)
{
volatile ccsr_local_t *ccm = (void *)(CONFIG_SYS_FSL_CORENET_CCM_ADDR);
gd->used_laws &= ~(1 << idx);
out_be32(&ccm->law[idx].lawar, 0);
out_be32(&ccm->law[idx].lawbarh, 0);
out_be32(&ccm->law[idx].lawbarl, 0);
/* Read back so that we sync the writes */
in_be32(&ccm->law[idx].lawar);
return;
}
static int get_law_entry(u8 i, struct law_entry *e)
{
volatile ccsr_local_t *ccm = (void *)(CONFIG_SYS_FSL_CORENET_CCM_ADDR);
u32 lawar;
lawar = in_be32(&ccm->law[i].lawar);
if (!(lawar & LAW_EN))
return 0;
e->addr = ((u64)in_be32(&ccm->law[i].lawbarh) << 32) |
in_be32(&ccm->law[i].lawbarl);
e->size = lawar & 0x3f;
e->trgt_id = (lawar >> 20) & 0xff;
return 1;
}
#else
void set_law(u8 idx, phys_addr_t addr, enum law_size sz, enum law_trgt_if id)
{
volatile u32 *base = (volatile u32 *)(CONFIG_SYS_IMMR + 0xc08);
volatile u32 *lawbar = base + 8 * idx;
volatile u32 *lawar = base + 8 * idx + 2;
gd->used_laws |= (1 << idx);
out_be32(lawar, 0);
out_be32(lawbar, addr >> 12);
out_be32(lawar, LAW_EN | ((u32)id << 20) | (u32)sz);
/* Read back so that we sync the writes */
in_be32(lawar);
}
void disable_law(u8 idx)
{
volatile u32 *base = (volatile u32 *)(CONFIG_SYS_IMMR + 0xc08);
volatile u32 *lawbar = base + 8 * idx;
volatile u32 *lawar = base + 8 * idx + 2;
gd->used_laws &= ~(1 << idx);
out_be32(lawar, 0);
out_be32(lawbar, 0);
/* Read back so that we sync the writes */
in_be32(lawar);
return;
}
static int get_law_entry(u8 i, struct law_entry *e)
{
volatile u32 *base = (volatile u32 *)(CONFIG_SYS_IMMR + 0xc08);
volatile u32 *lawbar = base + 8 * i;
volatile u32 *lawar = base + 8 * i + 2;
u32 temp;
temp = in_be32(lawar);
if (!(temp & LAW_EN))
return 0;
e->addr = (u64)in_be32(lawbar) << 12;
e->size = temp & 0x3f;
e->trgt_id = (temp >> 20) & 0xff;
return 1;
}
#endif
int set_next_law(phys_addr_t addr, enum law_size sz, enum law_trgt_if id)
{
u32 idx = ffz(gd->used_laws);
if (idx >= FSL_HW_NUM_LAWS)
return -1;
set_law(idx, addr, sz, id);
return idx;
}
#ifndef CONFIG_NAND_SPL
int set_last_law(phys_addr_t addr, enum law_size sz, enum law_trgt_if id)
{
u32 idx;
/* we have no LAWs free */
if (gd->used_laws == -1)
return -1;
/* grab the last free law */
idx = __ilog2(~(gd->used_laws));
if (idx >= FSL_HW_NUM_LAWS)
return -1;
set_law(idx, addr, sz, id);
return idx;
}
struct law_entry find_law(phys_addr_t addr)
{
struct law_entry entry;
int i;
entry.index = -1;
entry.addr = 0;
entry.size = 0;
entry.trgt_id = 0;
for (i = 0; i < FSL_HW_NUM_LAWS; i++) {
u64 upper;
if (!get_law_entry(i, &entry))
continue;
upper = entry.addr + (2ull << entry.size);
if ((addr >= entry.addr) && (addr < upper)) {
entry.index = i;
break;
}
}
return entry;
}
void print_laws(void)
{
volatile u32 *base = (volatile u32 *)(CONFIG_SYS_IMMR + 0xc08);
volatile u32 *lawbar = base;
volatile u32 *lawar = base + 2;
int i;
printf("\nLocal Access Window Configuration\n");
for(i = 0; i < FSL_HW_NUM_LAWS; i++) {
printf("\tLAWBAR%d : 0x%08x, LAWAR%d : 0x%08x\n",
i, in_be32(lawbar), i, in_be32(lawar));
lawbar += 8;
lawar += 8;
}
return;
}
/* use up to 2 LAWs for DDR, used the last available LAWs */
int 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;
if (set_last_law(start, law_sz_enc, id) < 0)
return -1;
/* 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;
if (set_last_law(start, law_sz_enc, id) < 0)
return -1;
} else {
return 0;
}
/* do we still have anything to map */
sz = sz - law_sz;
if (sz)
return 1;
return 0;
}
#endif
void init_laws(void)
{
int i;
#if FSL_HW_NUM_LAWS < 32
gd->used_laws = ~((1 << FSL_HW_NUM_LAWS) - 1);
#elif FSL_HW_NUM_LAWS == 32
gd->used_laws = 0;
#else
#error FSL_HW_NUM_LAWS can not be greater than 32 w/o code changes
#endif
for (i = 0; i < num_law_entries; i++) {
if (law_table[i].index == -1)
set_next_law(law_table[i].addr, law_table[i].size,
law_table[i].trgt_id);
else
set_law(law_table[i].index, law_table[i].addr,
law_table[i].size, law_table[i].trgt_id);
}
return ;
}