/* $OpenBSD: arml2cc.c,v 1.2 2013/05/02 20:57:37 patrick Exp $ */ /* * Copyright (c) 2013 Patrick Wildt * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define PL310_ERRATA_727915 /* offset from periphbase */ #define L2C_ADDR 0x2000 #define L2C_SIZE 0x1000 /* registers */ #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 *); 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) { return (1); } 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\n"); 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; } 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) { /* spin */ } } 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) { /* spin */ } } void arml2cc_cache_sync(struct arml2cc_softc *sc) { /* ARM Errata 753970 */ bus_space_write_4(sc->sc_iot, sc->sc_ioh, 0x740, 0xffffffff); } 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; // printf("l2inv op %x %08x %08x incr %d %d\n", cache_op, pa, endpa, line_size, len); 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); }