1*47a232c6SMichal Meloun /*- 2*47a232c6SMichal Meloun * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> 3*47a232c6SMichal Meloun * All rights reserved. 4*47a232c6SMichal Meloun * 5*47a232c6SMichal Meloun * Redistribution and use in source and binary forms, with or without 6*47a232c6SMichal Meloun * modification, are permitted provided that the following conditions 7*47a232c6SMichal Meloun * are met: 8*47a232c6SMichal Meloun * 1. Redistributions of source code must retain the above copyright 9*47a232c6SMichal Meloun * notice, this list of conditions and the following disclaimer. 10*47a232c6SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright 11*47a232c6SMichal Meloun * notice, this list of conditions and the following disclaimer in the 12*47a232c6SMichal Meloun * documentation and/or other materials provided with the distribution. 13*47a232c6SMichal Meloun * 14*47a232c6SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*47a232c6SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*47a232c6SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*47a232c6SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*47a232c6SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*47a232c6SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*47a232c6SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*47a232c6SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*47a232c6SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*47a232c6SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*47a232c6SMichal Meloun * SUCH DAMAGE. 25*47a232c6SMichal Meloun */ 26*47a232c6SMichal Meloun 27*47a232c6SMichal Meloun #include <sys/cdefs.h> 28*47a232c6SMichal Meloun __FBSDID("$FreeBSD$"); 29*47a232c6SMichal Meloun 30*47a232c6SMichal Meloun /* 31*47a232c6SMichal Meloun * Memory controller driver for Tegra SoCs. 32*47a232c6SMichal Meloun */ 33*47a232c6SMichal Meloun #include <sys/param.h> 34*47a232c6SMichal Meloun #include <sys/systm.h> 35*47a232c6SMichal Meloun #include <sys/bus.h> 36*47a232c6SMichal Meloun #include <sys/kernel.h> 37*47a232c6SMichal Meloun #include <sys/limits.h> 38*47a232c6SMichal Meloun #include <sys/lock.h> 39*47a232c6SMichal Meloun #include <sys/mutex.h> 40*47a232c6SMichal Meloun #include <sys/module.h> 41*47a232c6SMichal Meloun #include <sys/resource.h> 42*47a232c6SMichal Meloun 43*47a232c6SMichal Meloun #include <machine/bus.h> 44*47a232c6SMichal Meloun #include <machine/resource.h> 45*47a232c6SMichal Meloun #include <sys/rman.h> 46*47a232c6SMichal Meloun 47*47a232c6SMichal Meloun #include <dev/extres/clk/clk.h> 48*47a232c6SMichal Meloun #include <dev/ofw/ofw_bus.h> 49*47a232c6SMichal Meloun #include <dev/ofw/ofw_bus_subr.h> 50*47a232c6SMichal Meloun 51*47a232c6SMichal Meloun #include "clock_if.h" 52*47a232c6SMichal Meloun 53*47a232c6SMichal Meloun #define MC_INTSTATUS 0x000 54*47a232c6SMichal Meloun #define MC_INTMASK 0x004 55*47a232c6SMichal Meloun #define MC_INT_DECERR_MTS (1 << 16) 56*47a232c6SMichal Meloun #define MC_INT_SECERR_SEC (1 << 13) 57*47a232c6SMichal Meloun #define MC_INT_DECERR_VPR (1 << 12) 58*47a232c6SMichal Meloun #define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11) 59*47a232c6SMichal Meloun #define MC_INT_INVALID_SMMU_PAGE (1 << 10) 60*47a232c6SMichal Meloun #define MC_INT_ARBITRATION_EMEM (1 << 9) 61*47a232c6SMichal Meloun #define MC_INT_SECURITY_VIOLATION (1 << 8) 62*47a232c6SMichal Meloun #define MC_INT_DECERR_EMEM (1 << 6) 63*47a232c6SMichal Meloun #define MC_INT_INT_MASK (MC_INT_DECERR_MTS | \ 64*47a232c6SMichal Meloun MC_INT_SECERR_SEC | \ 65*47a232c6SMichal Meloun MC_INT_DECERR_VPR | \ 66*47a232c6SMichal Meloun MC_INT_INVALID_APB_ASID_UPDATE | \ 67*47a232c6SMichal Meloun MC_INT_INVALID_SMMU_PAGE | \ 68*47a232c6SMichal Meloun MC_INT_ARBITRATION_EMEM | \ 69*47a232c6SMichal Meloun MC_INT_SECURITY_VIOLATION | \ 70*47a232c6SMichal Meloun MC_INT_DECERR_EMEM) 71*47a232c6SMichal Meloun 72*47a232c6SMichal Meloun #define MC_ERR_STATUS 0x008 73*47a232c6SMichal Meloun #define MC_ERR_TYPE(x) (((x) >> 28) & 0x7) 74*47a232c6SMichal Meloun #define MC_ERR_TYPE_DECERR_EMEM 2 75*47a232c6SMichal Meloun #define MC_ERR_TYPE_SECURITY_TRUSTZONE 3 76*47a232c6SMichal Meloun #define MC_ERR_TYPE_SECURITY_CARVEOUT 4 77*47a232c6SMichal Meloun #define MC_ERR_TYPE_INVALID_SMMU_PAGE 6 78*47a232c6SMichal Meloun #define MC_ERR_INVALID_SMMU_PAGE_READABLE (1 << 27) 79*47a232c6SMichal Meloun #define MC_ERR_INVALID_SMMU_PAGE_WRITABLE (1 << 26) 80*47a232c6SMichal Meloun #define MC_ERR_INVALID_SMMU_PAGE_NONSECURE (1 << 25) 81*47a232c6SMichal Meloun #define MC_ERR_ADR_HI(x) (((x) >> 20) & 0x3) 82*47a232c6SMichal Meloun #define MC_ERR_SWAP (1 << 18) 83*47a232c6SMichal Meloun #define MC_ERR_SECURITY (1 << 17) 84*47a232c6SMichal Meloun #define MC_ERR_RW (1 << 16) 85*47a232c6SMichal Meloun #define MC_ERR_ADR1(x) (((x) >> 12) & 0x7) 86*47a232c6SMichal Meloun #define MC_ERR_ID(x) (((x) >> 0) & 07F) 87*47a232c6SMichal Meloun 88*47a232c6SMichal Meloun #define MC_ERR_ADDR 0x00C 89*47a232c6SMichal Meloun #define MC_EMEM_CFG 0x050 90*47a232c6SMichal Meloun #define MC_EMEM_ADR_CFG 0x054 91*47a232c6SMichal Meloun #define MC_EMEM_NUMDEV(x) (((x) >> 0 ) & 0x1) 92*47a232c6SMichal Meloun 93*47a232c6SMichal Meloun #define MC_EMEM_ADR_CFG_DEV0 0x058 94*47a232c6SMichal Meloun #define MC_EMEM_ADR_CFG_DEV1 0x05C 95*47a232c6SMichal Meloun #define EMEM_DEV_DEVSIZE(x) (((x) >> 16) & 0xF) 96*47a232c6SMichal Meloun #define EMEM_DEV_BANKWIDTH(x) (((x) >> 8) & 0x3) 97*47a232c6SMichal Meloun #define EMEM_DEV_COLWIDTH(x) (((x) >> 8) & 0x3) 98*47a232c6SMichal Meloun 99*47a232c6SMichal Meloun #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) 100*47a232c6SMichal Meloun #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) 101*47a232c6SMichal Meloun 102*47a232c6SMichal Meloun #define LOCK(_sc) mtx_lock(&(_sc)->mtx) 103*47a232c6SMichal Meloun #define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) 104*47a232c6SMichal Meloun #define SLEEP(_sc, timeout) mtx_sleep(sc, &sc->mtx, 0, "tegra_mc", timeout); 105*47a232c6SMichal Meloun #define LOCK_INIT(_sc) \ 106*47a232c6SMichal Meloun mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_mc", MTX_DEF) 107*47a232c6SMichal Meloun #define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx) 108*47a232c6SMichal Meloun #define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED) 109*47a232c6SMichal Meloun #define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED) 110*47a232c6SMichal Meloun 111*47a232c6SMichal Meloun static struct ofw_compat_data compat_data[] = { 112*47a232c6SMichal Meloun {"nvidia,tegra124-mc", 1}, 113*47a232c6SMichal Meloun {NULL, 0} 114*47a232c6SMichal Meloun }; 115*47a232c6SMichal Meloun 116*47a232c6SMichal Meloun struct tegra_mc_softc { 117*47a232c6SMichal Meloun device_t dev; 118*47a232c6SMichal Meloun struct mtx mtx; 119*47a232c6SMichal Meloun 120*47a232c6SMichal Meloun struct resource *mem_res; 121*47a232c6SMichal Meloun struct resource *irq_res; 122*47a232c6SMichal Meloun void *irq_h; 123*47a232c6SMichal Meloun 124*47a232c6SMichal Meloun clk_t clk; 125*47a232c6SMichal Meloun }; 126*47a232c6SMichal Meloun 127*47a232c6SMichal Meloun static char *smmu_err_tbl[16] = { 128*47a232c6SMichal Meloun "reserved", /* 0 */ 129*47a232c6SMichal Meloun "reserved", /* 1 */ 130*47a232c6SMichal Meloun "DRAM decode", /* 2 */ 131*47a232c6SMichal Meloun "Trustzome Security", /* 3 */ 132*47a232c6SMichal Meloun "Security carveout", /* 4 */ 133*47a232c6SMichal Meloun "reserved", /* 5 */ 134*47a232c6SMichal Meloun "Invalid SMMU page", /* 6 */ 135*47a232c6SMichal Meloun "reserved", /* 7 */ 136*47a232c6SMichal Meloun }; 137*47a232c6SMichal Meloun 138*47a232c6SMichal Meloun static void 139*47a232c6SMichal Meloun tegra_mc_intr(void *arg) 140*47a232c6SMichal Meloun { 141*47a232c6SMichal Meloun struct tegra_mc_softc *sc; 142*47a232c6SMichal Meloun uint32_t stat, err; 143*47a232c6SMichal Meloun uint64_t addr; 144*47a232c6SMichal Meloun 145*47a232c6SMichal Meloun sc = (struct tegra_mc_softc *)arg; 146*47a232c6SMichal Meloun 147*47a232c6SMichal Meloun stat = RD4(sc, MC_INTSTATUS); 148*47a232c6SMichal Meloun if ((stat & MC_INT_INT_MASK) == 0) { 149*47a232c6SMichal Meloun WR4(sc, MC_INTSTATUS, stat); 150*47a232c6SMichal Meloun return; 151*47a232c6SMichal Meloun } 152*47a232c6SMichal Meloun 153*47a232c6SMichal Meloun device_printf(sc->dev, "Memory Controller Interrupt:\n"); 154*47a232c6SMichal Meloun if (stat & MC_INT_DECERR_MTS) 155*47a232c6SMichal Meloun printf(" - MTS carveout violation\n"); 156*47a232c6SMichal Meloun if (stat & MC_INT_SECERR_SEC) 157*47a232c6SMichal Meloun printf(" - SEC carveout violation\n"); 158*47a232c6SMichal Meloun if (stat & MC_INT_DECERR_VPR) 159*47a232c6SMichal Meloun printf(" - VPR requirements violated\n"); 160*47a232c6SMichal Meloun if (stat & MC_INT_INVALID_APB_ASID_UPDATE) 161*47a232c6SMichal Meloun printf(" - ivalid APB ASID update\n"); 162*47a232c6SMichal Meloun if (stat & MC_INT_INVALID_SMMU_PAGE) 163*47a232c6SMichal Meloun printf(" - SMMU address translation error\n"); 164*47a232c6SMichal Meloun if (stat & MC_INT_ARBITRATION_EMEM) 165*47a232c6SMichal Meloun printf(" - arbitration deadlock-prevention threshold hit\n"); 166*47a232c6SMichal Meloun if (stat & MC_INT_SECURITY_VIOLATION) 167*47a232c6SMichal Meloun printf(" - SMMU address translation security error\n"); 168*47a232c6SMichal Meloun if (stat & MC_INT_DECERR_EMEM) 169*47a232c6SMichal Meloun printf(" - SMMU address decode error\n"); 170*47a232c6SMichal Meloun 171*47a232c6SMichal Meloun if ((stat & (MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | 172*47a232c6SMichal Meloun MC_INT_DECERR_EMEM)) != 0) { 173*47a232c6SMichal Meloun err = RD4(sc, MC_ERR_STATUS); 174*47a232c6SMichal Meloun addr = RD4(sc, MC_ERR_STATUS); 175*47a232c6SMichal Meloun addr |= (uint64_t)(MC_ERR_ADR_HI(err)) << 32; 176*47a232c6SMichal Meloun printf(" at 0x%012llX [%s %s %s] - %s error.\n", 177*47a232c6SMichal Meloun addr, 178*47a232c6SMichal Meloun stat & MC_ERR_SWAP ? "Swap, " : "", 179*47a232c6SMichal Meloun stat & MC_ERR_SECURITY ? "Sec, " : "", 180*47a232c6SMichal Meloun stat & MC_ERR_RW ? "Write" : "Read", 181*47a232c6SMichal Meloun smmu_err_tbl[MC_ERR_TYPE(err)]); 182*47a232c6SMichal Meloun } 183*47a232c6SMichal Meloun WR4(sc, MC_INTSTATUS, stat); 184*47a232c6SMichal Meloun } 185*47a232c6SMichal Meloun 186*47a232c6SMichal Meloun static void 187*47a232c6SMichal Meloun tegra_mc_init_hw(struct tegra_mc_softc *sc) 188*47a232c6SMichal Meloun { 189*47a232c6SMichal Meloun 190*47a232c6SMichal Meloun /* Disable and acknowledge all interrupts */ 191*47a232c6SMichal Meloun WR4(sc, MC_INTMASK, 0); 192*47a232c6SMichal Meloun WR4(sc, MC_INTSTATUS, MC_INT_INT_MASK); 193*47a232c6SMichal Meloun } 194*47a232c6SMichal Meloun 195*47a232c6SMichal Meloun static int 196*47a232c6SMichal Meloun tegra_mc_probe(device_t dev) 197*47a232c6SMichal Meloun { 198*47a232c6SMichal Meloun if (!ofw_bus_status_okay(dev)) 199*47a232c6SMichal Meloun return (ENXIO); 200*47a232c6SMichal Meloun 201*47a232c6SMichal Meloun if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 202*47a232c6SMichal Meloun return (ENXIO); 203*47a232c6SMichal Meloun device_set_desc(dev, "Tegra Memory Controller"); 204*47a232c6SMichal Meloun return (BUS_PROBE_DEFAULT); 205*47a232c6SMichal Meloun } 206*47a232c6SMichal Meloun 207*47a232c6SMichal Meloun static int 208*47a232c6SMichal Meloun tegra_mc_attach(device_t dev) 209*47a232c6SMichal Meloun { 210*47a232c6SMichal Meloun int rv, rid; 211*47a232c6SMichal Meloun struct tegra_mc_softc *sc; 212*47a232c6SMichal Meloun 213*47a232c6SMichal Meloun sc = device_get_softc(dev); 214*47a232c6SMichal Meloun sc->dev = dev; 215*47a232c6SMichal Meloun 216*47a232c6SMichal Meloun LOCK_INIT(sc); 217*47a232c6SMichal Meloun 218*47a232c6SMichal Meloun /* Get the memory resource for the register mapping. */ 219*47a232c6SMichal Meloun rid = 0; 220*47a232c6SMichal Meloun sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 221*47a232c6SMichal Meloun RF_ACTIVE); 222*47a232c6SMichal Meloun if (sc->mem_res == NULL) { 223*47a232c6SMichal Meloun device_printf(dev, "Cannot map registers.\n"); 224*47a232c6SMichal Meloun rv = ENXIO; 225*47a232c6SMichal Meloun goto fail; 226*47a232c6SMichal Meloun } 227*47a232c6SMichal Meloun 228*47a232c6SMichal Meloun /* Allocate our IRQ resource. */ 229*47a232c6SMichal Meloun rid = 0; 230*47a232c6SMichal Meloun sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 231*47a232c6SMichal Meloun RF_ACTIVE); 232*47a232c6SMichal Meloun if (sc->irq_res == NULL) { 233*47a232c6SMichal Meloun device_printf(dev, "Cannot allocate interrupt.\n"); 234*47a232c6SMichal Meloun rv = ENXIO; 235*47a232c6SMichal Meloun goto fail; 236*47a232c6SMichal Meloun } 237*47a232c6SMichal Meloun 238*47a232c6SMichal Meloun /* OFW resources. */ 239*47a232c6SMichal Meloun rv = clk_get_by_ofw_name(dev, 0, "mc", &sc->clk); 240*47a232c6SMichal Meloun if (rv != 0) { 241*47a232c6SMichal Meloun device_printf(dev, "Cannot get mc clock: %d\n", rv); 242*47a232c6SMichal Meloun goto fail; 243*47a232c6SMichal Meloun } 244*47a232c6SMichal Meloun rv = clk_enable(sc->clk); 245*47a232c6SMichal Meloun if (rv != 0) { 246*47a232c6SMichal Meloun device_printf(dev, "Cannot enable clock: %d\n", rv); 247*47a232c6SMichal Meloun goto fail; 248*47a232c6SMichal Meloun } 249*47a232c6SMichal Meloun 250*47a232c6SMichal Meloun /* Init hardware. */ 251*47a232c6SMichal Meloun tegra_mc_init_hw(sc); 252*47a232c6SMichal Meloun 253*47a232c6SMichal Meloun /* Setup interrupt */ 254*47a232c6SMichal Meloun rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 255*47a232c6SMichal Meloun NULL, tegra_mc_intr, sc, &sc->irq_h); 256*47a232c6SMichal Meloun if (rv) { 257*47a232c6SMichal Meloun device_printf(dev, "Cannot setup interrupt.\n"); 258*47a232c6SMichal Meloun goto fail; 259*47a232c6SMichal Meloun } 260*47a232c6SMichal Meloun 261*47a232c6SMichal Meloun /* Enable Interrupts */ 262*47a232c6SMichal Meloun WR4(sc, MC_INTMASK, MC_INT_INT_MASK); 263*47a232c6SMichal Meloun 264*47a232c6SMichal Meloun return (bus_generic_attach(dev)); 265*47a232c6SMichal Meloun 266*47a232c6SMichal Meloun fail: 267*47a232c6SMichal Meloun if (sc->clk != NULL) 268*47a232c6SMichal Meloun clk_release(sc->clk); 269*47a232c6SMichal Meloun if (sc->irq_h != NULL) 270*47a232c6SMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->irq_h); 271*47a232c6SMichal Meloun if (sc->irq_res != NULL) 272*47a232c6SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 273*47a232c6SMichal Meloun if (sc->mem_res != NULL) 274*47a232c6SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); 275*47a232c6SMichal Meloun LOCK_DESTROY(sc); 276*47a232c6SMichal Meloun 277*47a232c6SMichal Meloun return (rv); 278*47a232c6SMichal Meloun } 279*47a232c6SMichal Meloun 280*47a232c6SMichal Meloun static int 281*47a232c6SMichal Meloun tegra_mc_detach(device_t dev) 282*47a232c6SMichal Meloun { 283*47a232c6SMichal Meloun struct tegra_mc_softc *sc; 284*47a232c6SMichal Meloun 285*47a232c6SMichal Meloun sc = device_get_softc(dev); 286*47a232c6SMichal Meloun if (sc->irq_h != NULL) 287*47a232c6SMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->irq_h); 288*47a232c6SMichal Meloun if (sc->irq_res != NULL) 289*47a232c6SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 290*47a232c6SMichal Meloun if (sc->mem_res != NULL) 291*47a232c6SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); 292*47a232c6SMichal Meloun 293*47a232c6SMichal Meloun LOCK_DESTROY(sc); 294*47a232c6SMichal Meloun return (bus_generic_detach(dev)); 295*47a232c6SMichal Meloun } 296*47a232c6SMichal Meloun 297*47a232c6SMichal Meloun static device_method_t tegra_mc_methods[] = { 298*47a232c6SMichal Meloun /* Device interface */ 299*47a232c6SMichal Meloun DEVMETHOD(device_probe, tegra_mc_probe), 300*47a232c6SMichal Meloun DEVMETHOD(device_attach, tegra_mc_attach), 301*47a232c6SMichal Meloun DEVMETHOD(device_detach, tegra_mc_detach), 302*47a232c6SMichal Meloun 303*47a232c6SMichal Meloun 304*47a232c6SMichal Meloun DEVMETHOD_END 305*47a232c6SMichal Meloun }; 306*47a232c6SMichal Meloun 307*47a232c6SMichal Meloun static devclass_t tegra_mc_devclass; 308*47a232c6SMichal Meloun static DEFINE_CLASS_0(mc, tegra_mc_driver, tegra_mc_methods, 309*47a232c6SMichal Meloun sizeof(struct tegra_mc_softc)); 310*47a232c6SMichal Meloun DRIVER_MODULE(tegra_mc, simplebus, tegra_mc_driver, tegra_mc_devclass, 311*47a232c6SMichal Meloun NULL, NULL); 312