blob: 3beff780de0fdb2276d8c8db68d0c3c64da36d11 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Intel Corporation <www.intel.com>
*
*/
#include <dm.h>
#include <hang.h>
#include <wait_bit.h>
#include <asm/io.h>
#include <linux/bitops.h>
/* Directory */
#define DIRUSFER 0x80010
#define DIRUCASER0 0x80040
#define DIRUSFMCR 0x80080
#define DIRUSFMAR 0x80084
#define DIRUSFMCR_SFID_SHIFT 16
/* Coherent cache agent interface */
#define CAIUIDR 0x00ffc
#define CAIUIDR_CA_GET(v) (((v) & 0x00008000) >> 15)
#define CAIUIDR_TYPE_GET(v) (((v) & 0x000f0000) >> 16)
#define CAIUIDR_TYPE_ACE_CAI_DVM_SUPPORT 0
#define CAIUIDR_TYPE_ACELITE_CAI_DVM_SUPPORT 1
/* Coherent subsystem */
#define CSADSER0 0xff040
#define CSUIDR 0xffff8
#define CSIDR 0xffffc
#define CSUIDR_NUMCAIUS_GET(v) (((v) & 0x0000007f) >> 0)
#define CSUIDR_NUMDIRUS_GET(v) (((v) & 0x003f0000) >> 16)
#define CSUIDR_NUMCMIUS_GET(v) (((v) & 0x3f000000) >> 24)
#define CSIDR_NUMSFS_GET(v) (((v) & 0x007c0000) >> 18)
#define DIR_REG_SZ 0x1000
#define CAIU_REG_SZ 0x1000
#define CCU_DIR_REG_ADDR(base, reg, dir) \
((base) + (reg) + ((dir) * DIR_REG_SZ))
/* OCRAM firewall register */
#define OCRAM_FW_01 0x100204
#define OCRAM_SECURE_REGIONS 4
#define OCRAM_PRIVILEGED_MASK BIT(29)
#define OCRAM_SECURE_MASK BIT(30)
static void ncore_ccu_init_dirs(void __iomem *base)
{
ulong i, f;
int ret;
u32 num_of_dirs;
u32 num_of_snoop_filters;
u32 reg;
num_of_dirs = CSUIDR_NUMDIRUS_GET(readl(base + CSUIDR));
num_of_snoop_filters =
CSIDR_NUMSFS_GET(readl(base + CSIDR)) + 1;
/* Initialize each snoop filter in each directory */
for (f = 0; f < num_of_snoop_filters; f++) {
reg = f << DIRUSFMCR_SFID_SHIFT;
for (i = 0; i < num_of_dirs; i++) {
/* Initialize all entries */
writel(reg, CCU_DIR_REG_ADDR(base, DIRUSFMCR, i));
/* Poll snoop filter maintenance operation active
* bit become 0.
*/
ret = wait_for_bit_le32((const void *)
CCU_DIR_REG_ADDR(base,
DIRUSFMAR, i),
BIT(0), false, 1000, false);
if (ret) {
puts("CCU: Directory initialization failed!\n");
hang();
}
/* Enable snoop filter, a bit per snoop filter */
setbits_le32((ulong)CCU_DIR_REG_ADDR(base, DIRUSFER, i),
BIT(f));
}
}
}
static void ncore_ccu_init_coh_agent(void __iomem *base)
{
u32 num_of_coh_agent_intf;
u32 num_of_dirs;
u32 reg;
u32 type;
u32 i, dir;
num_of_coh_agent_intf =
CSUIDR_NUMCAIUS_GET(readl(base + CSUIDR));
num_of_dirs = CSUIDR_NUMDIRUS_GET(readl(base + CSUIDR));
for (i = 0; i < num_of_coh_agent_intf; i++) {
reg = readl(base + CAIUIDR + (i * CAIU_REG_SZ));
if (CAIUIDR_CA_GET(reg)) {
/* Caching agent bit is enabled, enable caching agent
* snoop in each directory
*/
for (dir = 0; dir < num_of_dirs; dir++) {
setbits_le32((ulong)
CCU_DIR_REG_ADDR(base, DIRUCASER0,
dir),
BIT(i));
}
}
type = CAIUIDR_TYPE_GET(reg);
if (type == CAIUIDR_TYPE_ACE_CAI_DVM_SUPPORT ||
type == CAIUIDR_TYPE_ACELITE_CAI_DVM_SUPPORT) {
/* DVM support is enabled, enable ACE DVM snoop*/
setbits_le32((ulong)(base + CSADSER0),
BIT(i));
}
}
}
static void ocram_bypass_firewall(void __iomem *base)
{
int i;
for (i = 0; i < OCRAM_SECURE_REGIONS; i++) {
clrbits_le32(base + OCRAM_FW_01 + (i * sizeof(u32)),
OCRAM_PRIVILEGED_MASK | OCRAM_SECURE_MASK);
}
}
static int ncore_ccu_probe(struct udevice *dev)
{
void __iomem *base;
fdt_addr_t addr;
addr = dev_read_addr(dev);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
base = (void __iomem *)addr;
ncore_ccu_init_dirs(base);
ncore_ccu_init_coh_agent(base);
ocram_bypass_firewall(base);
return 0;
}
static const struct udevice_id ncore_ccu_ids[] = {
{ .compatible = "arteris,ncore-ccu" },
{}
};
U_BOOT_DRIVER(ncore_ccu) = {
.name = "ncore_ccu",
.id = UCLASS_CACHE,
.of_match = ncore_ccu_ids,
.probe = ncore_ccu_probe,
.flags = DM_FLAG_PRE_RELOC,
};