#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <arm/cpufunc.h>
#include <arm/cortex/cortex.h>
#include <arm/cortex/smc.h>
#define PL310_ERRATA_727915
#define L2C_ADDR 0x2000
#define L2C_SIZE 0x1000
#define L2C_CACHE_ID 0x000
#define L2C_CACHE_TYPE 0x004
#define L2C_CTL 0x100
#define L2C_AUXCTL 0x104
#define L2C_TAG_RAM_CTL 0x108
#define L2C_DATA_RAM_CTL 0x10c
#define L2C_EVC_CTR_CTL 0x200
#define L2C_EVC_CTR0_CTL 0x204
#define L2C_EVC_CTR1_CTL 0x208
#define L2C_EVC_CTR0_VAL 0x20c
#define L2C_EVC_CTR1_VAL 0x210
#define L2C_INT_MASK 0x214
#define L2C_INT_MASK_STS 0x218
#define L2C_INT_RAW_STS 0x21c
#define L2C_INT_CLR 0x220
#define L2C_CACHE_SYNC 0x730
#define L2C_INV_PA 0x770
#define L2C_INV_WAY 0x77c
#define L2C_CLEAN_PA 0x7b0
#define L2C_CLEAN_INDEX 0x7b8
#define L2C_CLEAN_WAY 0x7bc
#define L2C_CLEAN_INV_PA 0x7f0
#define L2C_CLEAN_INV_INDEX 0x7f8
#define L2C_CLEAN_INV_WAY 0x7fc
#define L2C_D_LOCKDOWN0 0x900
#define L2C_I_LOCKDOWN0 0x904
#define L2C_D_LOCKDOWN1 0x908
#define L2C_I_LOCKDOWN1 0x90c
#define L2C_D_LOCKDOWN2 0x910
#define L2C_I_LOCKDOWN2 0x914
#define L2C_D_LOCKDOWN3 0x918
#define L2C_I_LOCKDOWN3 0x91c
#define L2C_D_LOCKDOWN4 0x920
#define L2C_I_LOCKDOWN4 0x924
#define L2C_D_LOCKDOWN5 0x928
#define L2C_I_LOCKDOWN5 0x92c
#define L2C_D_LOCKDOWN6 0x930
#define L2C_I_LOCKDOWN6 0x934
#define L2C_D_LOCKDOWN7 0x938
#define L2C_I_LOCKDOWN7 0x93c
#define L2C_LOCKDOWN_LINE_EN 0x950
#define L2C_UNLOCK_WAY 0x954
#define L2C_ADDR_FILTER_START 0xc00
#define L2C_ADDR_FILTER_END 0xc04
#define L2C_DEBUG_CTL 0xf40
#define L2C_PREFETCH_CTL 0xf60
#define L2C_POWER_CTL 0xf80
#define L2C_CACHE_ID_RELEASE_MASK 0x3f
#define L2C_CACHE_TYPE_LINESIZE 0x3
#define L2C_AUXCTL_ASSOC_SHIFT 16
#define L2C_AUXCTL_ASSOC_MASK 0x1
#define roundup2(size, unit) (((size) + (unit) - 1) & ~((unit) - 1))
struct arml2cc_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
uint32_t sc_enabled;
uint32_t sc_waymask;
uint32_t sc_dcache_line_size;
};
struct arml2cc_softc *arml2cc_sc;
int arml2cc_match(struct device *, void *, void *);
void arml2cc_attach(struct device *parent, struct device *self, void *args);
void arml2cc_enable(struct arml2cc_softc *);
void arml2cc_disable(struct arml2cc_softc *);
void arml2cc_sdcache_wbinv_all(void);
void arml2cc_sdcache_wbinv_range(vaddr_t, paddr_t, psize_t);
void arml2cc_sdcache_inv_range(vaddr_t, paddr_t, psize_t);
void arml2cc_sdcache_wb_range(vaddr_t, paddr_t, psize_t);
void arml2cc_cache_range_op(paddr_t, psize_t, bus_size_t);
void arml2cc_cache_way_op(struct arml2cc_softc *, bus_size_t, uint32_t);
void arml2cc_cache_op(struct arml2cc_softc *, bus_size_t, uint32_t);
void arml2cc_cache_sync(struct arml2cc_softc *);
void arml2cc_sdcache_drain_writebuf(void);
const struct cfattach armliicc_ca = {
sizeof (struct arml2cc_softc), arml2cc_match, arml2cc_attach
};
struct cfdriver armliicc_cd = {
NULL, "armliicc", DV_DULL
};
int
arml2cc_match(struct device *parent, void *cfdata, void *aux)
{
if ((cpufunc_id() & CPU_ID_CORTEX_A9_MASK) == CPU_ID_CORTEX_A9)
return (1);
return (0);
}
void
arml2cc_attach(struct device *parent, struct device *self, void *args)
{
struct cortex_attach_args *ia = args;
struct arml2cc_softc *sc = (struct arml2cc_softc *) self;
sc->sc_iot = ia->ca_iot;
if (bus_space_map(sc->sc_iot, ia->ca_periphbase + L2C_ADDR,
L2C_SIZE, 0, &sc->sc_ioh))
panic("arml2cc_attach: bus_space_map failed!");
printf(": rtl %d", bus_space_read_4(sc->sc_iot, sc->sc_ioh,
L2C_CACHE_ID) & 0x3f);
arml2cc_sc = sc;
if (bus_space_read_4(sc->sc_iot, sc->sc_ioh, L2C_CTL))
panic("L2 Cache controller was already enabled");
sc->sc_dcache_line_size = 32 << (bus_space_read_4(sc->sc_iot, sc->sc_ioh, L2C_CACHE_TYPE) & L2C_CACHE_TYPE_LINESIZE);
sc->sc_waymask = (8 << ((bus_space_read_4(sc->sc_iot, sc->sc_ioh, L2C_AUXCTL)
>> L2C_AUXCTL_ASSOC_SHIFT) & L2C_AUXCTL_ASSOC_MASK)) - 1;
printf(" waymask: 0x%08x\n", sc->sc_waymask);
arml2cc_enable(sc);
sc->sc_enabled = 1;
arml2cc_sdcache_wbinv_all();
cpufuncs.cf_sdcache_wbinv_all = arml2cc_sdcache_wbinv_all;
cpufuncs.cf_sdcache_wbinv_range = arml2cc_sdcache_wbinv_range;
cpufuncs.cf_sdcache_inv_range = arml2cc_sdcache_inv_range;
cpufuncs.cf_sdcache_wb_range = arml2cc_sdcache_wb_range;
cpufuncs.cf_sdcache_drain_writebuf = arml2cc_sdcache_drain_writebuf;
}
void
arml2cc_enable(struct arml2cc_softc *sc)
{
int s;
s = splhigh();
platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_CTL, SMC_L2_CTL,
1);
arml2cc_cache_way_op(sc, L2C_INV_WAY, sc->sc_waymask);
arml2cc_cache_sync(sc);
splx(s);
}
void
arml2cc_disable(struct arml2cc_softc *sc)
{
int s;
s = splhigh();
arml2cc_cache_way_op(sc, L2C_CLEAN_INV_WAY, sc->sc_waymask);
arml2cc_cache_sync(sc);
platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_CTL, SMC_L2_CTL, 0);
splx(s);
}
void
arml2cc_cache_op(struct arml2cc_softc *sc, bus_size_t off, uint32_t val)
{
bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val);
while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, off) & 1) {
}
}
void
arml2cc_cache_way_op(struct arml2cc_softc *sc, bus_size_t off, uint32_t way_mask)
{
bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, way_mask);
while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, off) & way_mask) {
}
}
void
arml2cc_cache_sync(struct arml2cc_softc *sc)
{
bus_space_write_4(sc->sc_iot, sc->sc_ioh, 0x740, 0xffffffff);
}
void
arml2cc_sdcache_drain_writebuf(void)
{
struct arml2cc_softc * const sc = arml2cc_sc;
if (sc == NULL || !sc->sc_enabled)
return;
arml2cc_cache_sync(sc);
}
void
arml2cc_cache_range_op(paddr_t pa, psize_t len, bus_size_t cache_op)
{
struct arml2cc_softc * const sc = arml2cc_sc;
size_t line_size = sc->sc_dcache_line_size;
size_t line_mask = line_size - 1;
paddr_t endpa;
endpa = pa + len;
pa = pa & ~line_mask;
while (endpa > pa) {
arml2cc_cache_op(sc, cache_op, pa);
pa += line_size;
}
}
void
arml2cc_sdcache_wbinv_all(void)
{
struct arml2cc_softc *sc = arml2cc_sc;
if (sc == NULL || !sc->sc_enabled)
return;
#ifdef PL310_ERRATA_727915
platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_DEBUG_CTL, SMC_L2_DBG, 3);
#endif
bus_space_write_4(sc->sc_iot, sc->sc_ioh, L2C_CLEAN_INV_WAY, sc->sc_waymask);
while(bus_space_read_4(sc->sc_iot, sc->sc_ioh, L2C_CLEAN_INV_WAY) & sc->sc_waymask);
#ifdef PL310_ERRATA_727915
platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_DEBUG_CTL, SMC_L2_DBG, 0);
#endif
arml2cc_cache_sync(sc);
}
void
arml2cc_sdcache_wbinv_range(vaddr_t va, paddr_t pa, psize_t len)
{
struct arml2cc_softc *sc = arml2cc_sc;
if (sc == NULL || !sc->sc_enabled)
return;
#ifdef PL310_ERRATA_727915
platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_DEBUG_CTL, SMC_L2_DBG, 3);
#endif
arml2cc_cache_range_op(pa, len, L2C_CLEAN_INV_PA);
arml2cc_cache_sync(sc);
#ifdef PL310_ERRATA_727915
platform_smc_write(sc->sc_iot, sc->sc_ioh, L2C_DEBUG_CTL, SMC_L2_DBG, 0);
#endif
}
void
arml2cc_sdcache_inv_range(vaddr_t va, paddr_t pa, psize_t len)
{
struct arml2cc_softc *sc = arml2cc_sc;
if (sc == NULL || !sc->sc_enabled)
return;
arml2cc_cache_range_op(pa, len, L2C_INV_PA);
arml2cc_cache_sync(sc);
}
void
arml2cc_sdcache_wb_range(vaddr_t va, paddr_t pa, psize_t len)
{
struct arml2cc_softc *sc = arml2cc_sc;
if (sc == NULL || !sc->sc_enabled)
return;
arml2cc_cache_range_op(pa, len, L2C_CLEAN_PA);
arml2cc_cache_sync(sc);
}