Path: blob/main/sys/arm/broadcom/bcm2835/bcm2836.c
39566 views
/*1* Copyright 2015 Andrew Turner.2* Copyright 2016 Svatopluk Kraus3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions are7* met:8*9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR18* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE19* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR20* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF21* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR22* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,23* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR24* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF25* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.26*/2728#include <sys/cdefs.h>29#include "opt_platform.h"3031#include <sys/param.h>32#include <sys/systm.h>33#include <sys/bus.h>34#include <sys/cpuset.h>35#include <sys/kernel.h>36#include <sys/lock.h>37#include <sys/module.h>38#include <sys/mutex.h>39#include <sys/proc.h>40#include <sys/rman.h>41#ifdef SMP42#include <sys/smp.h>43#endif4445#include <machine/bus.h>46#include <machine/intr.h>47#include <machine/resource.h>48#ifdef SMP49#include <machine/smp.h>50#endif5152#include <dev/ofw/ofw_bus_subr.h>53#include <dev/ofw/ofw_bus.h>5455#include "pic_if.h"5657#define BCM_LINTC_CONTROL_REG 0x0058#define BCM_LINTC_PRESCALER_REG 0x0859#define BCM_LINTC_GPU_ROUTING_REG 0x0c60#define BCM_LINTC_PMU_ROUTING_SET_REG 0x1061#define BCM_LINTC_PMU_ROUTING_CLR_REG 0x1462#define BCM_LINTC_TIMER_CFG_REG(n) (0x40 + (n) * 4)63#define BCM_LINTC_MBOX_CFG_REG(n) (0x50 + (n) * 4)64#define BCM_LINTC_PENDING_REG(n) (0x60 + (n) * 4)65#define BCM_LINTC_MBOX0_SET_REG(n) (0x80 + (n) * 16)66#define BCM_LINTC_MBOX1_SET_REG(n) (0x84 + (n) * 16)67#define BCM_LINTC_MBOX2_SET_REG(n) (0x88 + (n) * 16)68#define BCM_LINTC_MBOX3_SET_REG(n) (0x8C + (n) * 16)69#define BCM_LINTC_MBOX0_CLR_REG(n) (0xC0 + (n) * 16)70#define BCM_LINTC_MBOX1_CLR_REG(n) (0xC4 + (n) * 16)71#define BCM_LINTC_MBOX2_CLR_REG(n) (0xC8 + (n) * 16)72#define BCM_LINTC_MBOX3_CLR_REG(n) (0xCC + (n) * 16)7374/* Prescaler Register */75#define BCM_LINTC_PSR_19_2 0x80000000 /* 19.2 MHz */7677/* GPU Interrupt Routing Register */78#define BCM_LINTC_GIRR_IRQ_CORE(n) (n)79#define BCM_LINTC_GIRR_FIQ_CORE(n) ((n) << 2)8081/* PMU Interrupt Routing Register */82#define BCM_LINTC_PIRR_IRQ_EN_CORE(n) (1 << (n))83#define BCM_LINTC_PIRR_FIQ_EN_CORE(n) (1 << ((n) + 4))8485/* Timer Config Register */86#define BCM_LINTC_TCR_IRQ_EN_TIMER(n) (1 << (n))87#define BCM_LINTC_TCR_FIQ_EN_TIMER(n) (1 << ((n) + 4))8889/* MBOX Config Register */90#define BCM_LINTC_MCR_IRQ_EN_MBOX(n) (1 << (n))91#define BCM_LINTC_MCR_FIQ_EN_MBOX(n) (1 << ((n) + 4))9293#define BCM_LINTC_CNTPSIRQ_IRQ 094#define BCM_LINTC_CNTPNSIRQ_IRQ 195#define BCM_LINTC_CNTHPIRQ_IRQ 296#define BCM_LINTC_CNTVIRQ_IRQ 397#define BCM_LINTC_MBOX0_IRQ 498#define BCM_LINTC_MBOX1_IRQ 599#define BCM_LINTC_MBOX2_IRQ 6100#define BCM_LINTC_MBOX3_IRQ 7101#define BCM_LINTC_GPU_IRQ 8102#define BCM_LINTC_PMU_IRQ 9103#define BCM_LINTC_AXI_IRQ 10104#define BCM_LINTC_LTIMER_IRQ 11105106#define BCM_LINTC_NIRQS 12107108#define BCM_LINTC_TIMER0_IRQ BCM_LINTC_CNTPSIRQ_IRQ109#define BCM_LINTC_TIMER1_IRQ BCM_LINTC_CNTPNSIRQ_IRQ110#define BCM_LINTC_TIMER2_IRQ BCM_LINTC_CNTHPIRQ_IRQ111#define BCM_LINTC_TIMER3_IRQ BCM_LINTC_CNTVIRQ_IRQ112113#define BCM_LINTC_TIMER0_IRQ_MASK (1 << BCM_LINTC_TIMER0_IRQ)114#define BCM_LINTC_TIMER1_IRQ_MASK (1 << BCM_LINTC_TIMER1_IRQ)115#define BCM_LINTC_TIMER2_IRQ_MASK (1 << BCM_LINTC_TIMER2_IRQ)116#define BCM_LINTC_TIMER3_IRQ_MASK (1 << BCM_LINTC_TIMER3_IRQ)117#define BCM_LINTC_MBOX0_IRQ_MASK (1 << BCM_LINTC_MBOX0_IRQ)118#define BCM_LINTC_GPU_IRQ_MASK (1 << BCM_LINTC_GPU_IRQ)119#define BCM_LINTC_PMU_IRQ_MASK (1 << BCM_LINTC_PMU_IRQ)120121#define BCM_LINTC_UP_PENDING_MASK \122(BCM_LINTC_TIMER0_IRQ_MASK | \123BCM_LINTC_TIMER1_IRQ_MASK | \124BCM_LINTC_TIMER2_IRQ_MASK | \125BCM_LINTC_TIMER3_IRQ_MASK | \126BCM_LINTC_GPU_IRQ_MASK | \127BCM_LINTC_PMU_IRQ_MASK)128129#define BCM_LINTC_SMP_PENDING_MASK \130(BCM_LINTC_UP_PENDING_MASK | \131BCM_LINTC_MBOX0_IRQ_MASK)132133#ifdef SMP134#define BCM_LINTC_PENDING_MASK BCM_LINTC_SMP_PENDING_MASK135#else136#define BCM_LINTC_PENDING_MASK BCM_LINTC_UP_PENDING_MASK137#endif138139struct bcm_lintc_irqsrc {140struct intr_irqsrc bli_isrc;141u_int bli_irq;142union {143u_int bli_mask; /* for timers */144u_int bli_value; /* for GPU */145};146};147148struct bcm_lintc_softc {149device_t bls_dev;150struct mtx bls_mtx;151struct resource * bls_mem;152bus_space_tag_t bls_bst;153bus_space_handle_t bls_bsh;154struct bcm_lintc_irqsrc bls_isrcs[BCM_LINTC_NIRQS];155};156157static struct bcm_lintc_softc *bcm_lintc_sc;158159#ifdef SMP160#define BCM_LINTC_NIPIS 32 /* only mailbox 0 is used for IPI */161CTASSERT(INTR_IPI_COUNT <= BCM_LINTC_NIPIS);162#endif163164#define BCM_LINTC_LOCK(sc) mtx_lock_spin(&(sc)->bls_mtx)165#define BCM_LINTC_UNLOCK(sc) mtx_unlock_spin(&(sc)->bls_mtx)166#define BCM_LINTC_LOCK_INIT(sc) mtx_init(&(sc)->bls_mtx, \167device_get_nameunit((sc)->bls_dev), "bmc_local_intc", MTX_SPIN)168#define BCM_LINTC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->bls_mtx)169170#define bcm_lintc_read_4(sc, reg) \171bus_space_read_4((sc)->bls_bst, (sc)->bls_bsh, (reg))172#define bcm_lintc_write_4(sc, reg, val) \173bus_space_write_4((sc)->bls_bst, (sc)->bls_bsh, (reg), (val))174175static inline void176bcm_lintc_rwreg_clr(struct bcm_lintc_softc *sc, uint32_t reg,177uint32_t mask)178{179180bcm_lintc_write_4(sc, reg, bcm_lintc_read_4(sc, reg) & ~mask);181}182183static inline void184bcm_lintc_rwreg_set(struct bcm_lintc_softc *sc, uint32_t reg,185uint32_t mask)186{187188bcm_lintc_write_4(sc, reg, bcm_lintc_read_4(sc, reg) | mask);189}190191static void192bcm_lintc_timer_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)193{194cpuset_t *cpus;195uint32_t cpu;196197cpus = &bli->bli_isrc.isrc_cpu;198199BCM_LINTC_LOCK(sc);200for (cpu = 0; cpu < 4; cpu++)201if (CPU_ISSET(cpu, cpus))202bcm_lintc_rwreg_clr(sc, BCM_LINTC_TIMER_CFG_REG(cpu),203bli->bli_mask);204BCM_LINTC_UNLOCK(sc);205}206207static void208bcm_lintc_timer_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)209{210cpuset_t *cpus;211uint32_t cpu;212213cpus = &bli->bli_isrc.isrc_cpu;214215BCM_LINTC_LOCK(sc);216for (cpu = 0; cpu < 4; cpu++)217if (CPU_ISSET(cpu, cpus))218bcm_lintc_rwreg_set(sc, BCM_LINTC_TIMER_CFG_REG(cpu),219bli->bli_mask);220BCM_LINTC_UNLOCK(sc);221}222223static inline void224bcm_lintc_gpu_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)225{226227/* It's accessed just and only by one core. */228bcm_lintc_write_4(sc, BCM_LINTC_GPU_ROUTING_REG, 0);229}230231static inline void232bcm_lintc_gpu_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)233{234235/* It's accessed just and only by one core. */236bcm_lintc_write_4(sc, BCM_LINTC_GPU_ROUTING_REG, bli->bli_value);237}238239static inline void240bcm_lintc_pmu_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)241{242cpuset_t *cpus;243uint32_t cpu, mask;244245mask = 0;246cpus = &bli->bli_isrc.isrc_cpu;247248BCM_LINTC_LOCK(sc);249for (cpu = 0; cpu < 4; cpu++)250if (CPU_ISSET(cpu, cpus))251mask |= BCM_LINTC_PIRR_IRQ_EN_CORE(cpu);252/* Write-clear register. */253bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_CLR_REG, mask);254BCM_LINTC_UNLOCK(sc);255}256257static inline void258bcm_lintc_pmu_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)259{260cpuset_t *cpus;261uint32_t cpu, mask;262263mask = 0;264cpus = &bli->bli_isrc.isrc_cpu;265266BCM_LINTC_LOCK(sc);267for (cpu = 0; cpu < 4; cpu++)268if (CPU_ISSET(cpu, cpus))269mask |= BCM_LINTC_PIRR_IRQ_EN_CORE(cpu);270/* Write-set register. */271bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_SET_REG, mask);272BCM_LINTC_UNLOCK(sc);273}274275static void276bcm_lintc_mask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)277{278279switch (bli->bli_irq) {280case BCM_LINTC_TIMER0_IRQ:281case BCM_LINTC_TIMER1_IRQ:282case BCM_LINTC_TIMER2_IRQ:283case BCM_LINTC_TIMER3_IRQ:284bcm_lintc_timer_mask(sc, bli);285return;286case BCM_LINTC_MBOX0_IRQ:287case BCM_LINTC_MBOX1_IRQ:288case BCM_LINTC_MBOX2_IRQ:289case BCM_LINTC_MBOX3_IRQ:290return;291case BCM_LINTC_GPU_IRQ:292bcm_lintc_gpu_mask(sc, bli);293return;294case BCM_LINTC_PMU_IRQ:295bcm_lintc_pmu_mask(sc, bli);296return;297default:298panic("%s: not implemented for irq %u", __func__, bli->bli_irq);299}300}301302static void303bcm_lintc_unmask(struct bcm_lintc_softc *sc, struct bcm_lintc_irqsrc *bli)304{305306switch (bli->bli_irq) {307case BCM_LINTC_TIMER0_IRQ:308case BCM_LINTC_TIMER1_IRQ:309case BCM_LINTC_TIMER2_IRQ:310case BCM_LINTC_TIMER3_IRQ:311bcm_lintc_timer_unmask(sc, bli);312return;313case BCM_LINTC_MBOX0_IRQ:314case BCM_LINTC_MBOX1_IRQ:315case BCM_LINTC_MBOX2_IRQ:316case BCM_LINTC_MBOX3_IRQ:317return;318case BCM_LINTC_GPU_IRQ:319bcm_lintc_gpu_unmask(sc, bli);320return;321case BCM_LINTC_PMU_IRQ:322bcm_lintc_pmu_unmask(sc, bli);323return;324default:325panic("%s: not implemented for irq %u", __func__, bli->bli_irq);326}327}328329#ifdef SMP330static inline void331bcm_lintc_ipi_write(struct bcm_lintc_softc *sc, cpuset_t cpus, u_int ipi)332{333u_int cpu;334uint32_t mask;335336mask = 1 << ipi;337for (cpu = 0; cpu < mp_ncpus; cpu++)338if (CPU_ISSET(cpu, &cpus))339bcm_lintc_write_4(sc, BCM_LINTC_MBOX0_SET_REG(cpu),340mask);341}342343static inline void344bcm_lintc_ipi_dispatch(struct bcm_lintc_softc *sc, u_int cpu,345struct trapframe *tf)346{347u_int ipi;348uint32_t mask;349350mask = bcm_lintc_read_4(sc, BCM_LINTC_MBOX0_CLR_REG(cpu));351if (mask == 0) {352device_printf(sc->bls_dev, "Spurious ipi detected\n");353return;354}355356for (ipi = 0; mask != 0; mask >>= 1, ipi++) {357if ((mask & 0x01) == 0)358continue;359/*360* Clear an IPI before dispatching to not miss anyone361* and make sure that it's observed by everybody.362*/363bcm_lintc_write_4(sc, BCM_LINTC_MBOX0_CLR_REG(cpu), 1 << ipi);364#if defined(__aarch64__)365dsb(sy);366#else367dsb();368#endif369intr_ipi_dispatch(ipi);370}371}372#endif373374static inline void375bcm_lintc_irq_dispatch(struct bcm_lintc_softc *sc, u_int irq,376struct trapframe *tf)377{378struct bcm_lintc_irqsrc *bli;379380bli = &sc->bls_isrcs[irq];381if (intr_isrc_dispatch(&bli->bli_isrc, tf) != 0)382device_printf(sc->bls_dev, "Stray irq %u detected\n", irq);383}384385static int386bcm_lintc_intr(void *arg)387{388struct bcm_lintc_softc *sc;389u_int cpu;390uint32_t num, reg;391struct trapframe *tf;392393sc = arg;394cpu = PCPU_GET(cpuid);395tf = curthread->td_intr_frame;396397for (num = 0; ; num++) {398reg = bcm_lintc_read_4(sc, BCM_LINTC_PENDING_REG(cpu));399if ((reg & BCM_LINTC_PENDING_MASK) == 0)400break;401#ifdef SMP402if (reg & BCM_LINTC_MBOX0_IRQ_MASK)403bcm_lintc_ipi_dispatch(sc, cpu, tf);404#endif405if (reg & BCM_LINTC_TIMER0_IRQ_MASK)406bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER0_IRQ, tf);407if (reg & BCM_LINTC_TIMER1_IRQ_MASK)408bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER1_IRQ, tf);409if (reg & BCM_LINTC_TIMER2_IRQ_MASK)410bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER2_IRQ, tf);411if (reg & BCM_LINTC_TIMER3_IRQ_MASK)412bcm_lintc_irq_dispatch(sc, BCM_LINTC_TIMER3_IRQ, tf);413if (reg & BCM_LINTC_GPU_IRQ_MASK)414bcm_lintc_irq_dispatch(sc, BCM_LINTC_GPU_IRQ, tf);415if (reg & BCM_LINTC_PMU_IRQ_MASK)416bcm_lintc_irq_dispatch(sc, BCM_LINTC_PMU_IRQ, tf);417418arm_irq_memory_barrier(0); /* XXX */419}420reg &= ~BCM_LINTC_PENDING_MASK;421if (reg != 0)422device_printf(sc->bls_dev, "Unknown interrupt(s) %x\n", reg);423else if (num == 0 && bootverbose)424device_printf(sc->bls_dev, "Spurious interrupt detected\n");425426return (FILTER_HANDLED);427}428429static void430bcm_lintc_disable_intr(device_t dev, struct intr_irqsrc *isrc)431{432433bcm_lintc_mask(device_get_softc(dev), (struct bcm_lintc_irqsrc *)isrc);434}435436static void437bcm_lintc_enable_intr(device_t dev, struct intr_irqsrc *isrc)438{439struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc;440441arm_irq_memory_barrier(bli->bli_irq);442bcm_lintc_unmask(device_get_softc(dev), bli);443}444445static int446bcm_lintc_map_intr(device_t dev, struct intr_map_data *data,447struct intr_irqsrc **isrcp)448{449struct intr_map_data_fdt *daf;450struct bcm_lintc_softc *sc;451452if (data->type != INTR_MAP_DATA_FDT)453return (ENOTSUP);454455daf = (struct intr_map_data_fdt *)data;456if (daf->ncells > 2 || daf->cells[0] >= BCM_LINTC_NIRQS)457return (EINVAL);458459/* TODO: handle IRQ type here */460461sc = device_get_softc(dev);462*isrcp = &sc->bls_isrcs[daf->cells[0]].bli_isrc;463return (0);464}465466static void467bcm_lintc_pre_ithread(device_t dev, struct intr_irqsrc *isrc)468{469struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc;470471if (bli->bli_irq == BCM_LINTC_GPU_IRQ)472bcm_lintc_gpu_mask(device_get_softc(dev), bli);473else {474/*475* Handler for PPI interrupt does not make sense much unless476* there is one bound ithread for each core for it. Thus the477* interrupt can be masked on current core only while ithread478* bounded to this core ensures unmasking on the same core.479*/480panic ("%s: handlers are not supported", __func__);481}482}483484static void485bcm_lintc_post_ithread(device_t dev, struct intr_irqsrc *isrc)486{487struct bcm_lintc_irqsrc *bli = (struct bcm_lintc_irqsrc *)isrc;488489if (bli->bli_irq == BCM_LINTC_GPU_IRQ)490bcm_lintc_gpu_unmask(device_get_softc(dev), bli);491else {492/* See comment in bcm_lintc_pre_ithread(). */493panic ("%s: handlers are not supported", __func__);494}495}496497static void498bcm_lintc_post_filter(device_t dev, struct intr_irqsrc *isrc)499{500}501502static int503bcm_lintc_setup_intr(device_t dev, struct intr_irqsrc *isrc,504struct resource *res, struct intr_map_data *data)505{506struct bcm_lintc_softc *sc;507508if (isrc->isrc_handlers == 0 && isrc->isrc_flags & INTR_ISRCF_PPI) {509sc = device_get_softc(dev);510BCM_LINTC_LOCK(sc);511CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu);512BCM_LINTC_UNLOCK(sc);513}514return (0);515}516517#ifdef SMP518static void519bcm_lintc_init_rwreg_on_ap(struct bcm_lintc_softc *sc, u_int cpu, u_int irq,520uint32_t reg, uint32_t mask)521{522523if (intr_isrc_init_on_cpu(&sc->bls_isrcs[irq].bli_isrc, cpu))524bcm_lintc_rwreg_set(sc, reg, mask);525}526527static void528bcm_lintc_init_pmu_on_ap(struct bcm_lintc_softc *sc, u_int cpu)529{530struct intr_irqsrc *isrc = &sc->bls_isrcs[BCM_LINTC_PMU_IRQ].bli_isrc;531532if (intr_isrc_init_on_cpu(isrc, cpu)) {533/* Write-set register. */534bcm_lintc_write_4(sc, BCM_LINTC_PMU_ROUTING_SET_REG,535BCM_LINTC_PIRR_IRQ_EN_CORE(cpu));536}537}538539static void540bcm_lintc_init_secondary(device_t dev, uint32_t rootnum)541{542u_int cpu;543struct bcm_lintc_softc *sc;544545cpu = PCPU_GET(cpuid);546sc = device_get_softc(dev);547548BCM_LINTC_LOCK(sc);549bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER0_IRQ,550BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(0));551bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER1_IRQ,552BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(1));553bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER2_IRQ,554BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(2));555bcm_lintc_init_rwreg_on_ap(sc, cpu, BCM_LINTC_TIMER3_IRQ,556BCM_LINTC_TIMER_CFG_REG(cpu), BCM_LINTC_TCR_IRQ_EN_TIMER(3));557bcm_lintc_init_pmu_on_ap(sc, cpu);558BCM_LINTC_UNLOCK(sc);559}560561static void562bcm_lintc_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus,563u_int ipi)564{565struct bcm_lintc_softc *sc = device_get_softc(dev);566567KASSERT(isrc == &sc->bls_isrcs[BCM_LINTC_MBOX0_IRQ].bli_isrc,568("%s: bad ISRC %p argument", __func__, isrc));569bcm_lintc_ipi_write(sc, cpus, ipi);570}571572static int573bcm_lintc_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp)574{575struct bcm_lintc_softc *sc = device_get_softc(dev);576577KASSERT(ipi < BCM_LINTC_NIPIS, ("%s: too high ipi %u", __func__, ipi));578579*isrcp = &sc->bls_isrcs[BCM_LINTC_MBOX0_IRQ].bli_isrc;580return (0);581}582#endif583584static int585bcm_lintc_pic_attach(struct bcm_lintc_softc *sc)586{587struct bcm_lintc_irqsrc *bisrcs;588struct intr_pic *pic;589int error;590u_int flags;591uint32_t irq;592const char *name;593intptr_t xref;594595bisrcs = sc->bls_isrcs;596name = device_get_nameunit(sc->bls_dev);597for (irq = 0; irq < BCM_LINTC_NIRQS; irq++) {598bisrcs[irq].bli_irq = irq;599switch (irq) {600case BCM_LINTC_TIMER0_IRQ:601bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(0);602flags = INTR_ISRCF_PPI;603break;604case BCM_LINTC_TIMER1_IRQ:605bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(1);606flags = INTR_ISRCF_PPI;607break;608case BCM_LINTC_TIMER2_IRQ:609bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(2);610flags = INTR_ISRCF_PPI;611break;612case BCM_LINTC_TIMER3_IRQ:613bisrcs[irq].bli_mask = BCM_LINTC_TCR_IRQ_EN_TIMER(3);614flags = INTR_ISRCF_PPI;615break;616case BCM_LINTC_MBOX0_IRQ:617case BCM_LINTC_MBOX1_IRQ:618case BCM_LINTC_MBOX2_IRQ:619case BCM_LINTC_MBOX3_IRQ:620bisrcs[irq].bli_value = 0; /* not used */621flags = INTR_ISRCF_IPI;622break;623case BCM_LINTC_GPU_IRQ:624bisrcs[irq].bli_value = BCM_LINTC_GIRR_IRQ_CORE(0);625flags = 0;626break;627case BCM_LINTC_PMU_IRQ:628bisrcs[irq].bli_value = 0; /* not used */629flags = INTR_ISRCF_PPI;630break;631default:632bisrcs[irq].bli_value = 0; /* not used */633flags = 0;634break;635}636637error = intr_isrc_register(&bisrcs[irq].bli_isrc, sc->bls_dev,638flags, "%s,%u", name, irq);639if (error != 0)640return (error);641}642643xref = OF_xref_from_node(ofw_bus_get_node(sc->bls_dev));644pic = intr_pic_register(sc->bls_dev, xref);645if (pic == NULL)646return (ENXIO);647648error = intr_pic_claim_root(sc->bls_dev, xref, bcm_lintc_intr, sc,649INTR_ROOT_IRQ);650if (error != 0)651return (error);652653#ifdef SMP654error = intr_ipi_pic_register(sc->bls_dev, 0);655if (error != 0)656return (error);657#endif658659return (0);660}661662static int663bcm_lintc_probe(device_t dev)664{665666if (!ofw_bus_status_okay(dev))667return (ENXIO);668669if (!ofw_bus_is_compatible(dev, "brcm,bcm2836-l1-intc"))670return (ENXIO);671if (!ofw_bus_has_prop(dev, "interrupt-controller"))672return (ENXIO);673device_set_desc(dev, "BCM2836 Interrupt Controller");674return (BUS_PROBE_DEFAULT);675}676677static int678bcm_lintc_attach(device_t dev)679{680struct bcm_lintc_softc *sc;681int cpu, rid;682683sc = device_get_softc(dev);684685sc->bls_dev = dev;686if (bcm_lintc_sc != NULL)687return (ENXIO);688689rid = 0;690sc->bls_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,691RF_ACTIVE);692if (sc->bls_mem == NULL) {693device_printf(dev, "could not allocate memory resource\n");694return (ENXIO);695}696697sc->bls_bst = rman_get_bustag(sc->bls_mem);698sc->bls_bsh = rman_get_bushandle(sc->bls_mem);699700bcm_lintc_write_4(sc, BCM_LINTC_CONTROL_REG, 0);701bcm_lintc_write_4(sc, BCM_LINTC_PRESCALER_REG, BCM_LINTC_PSR_19_2);702703/* Disable all timers on all cores. */704for (cpu = 0; cpu < 4; cpu++)705bcm_lintc_write_4(sc, BCM_LINTC_TIMER_CFG_REG(cpu), 0);706707#ifdef SMP708/* Enable mailbox 0 on all cores used for IPI. */709for (cpu = 0; cpu < 4; cpu++)710bcm_lintc_write_4(sc, BCM_LINTC_MBOX_CFG_REG(cpu),711BCM_LINTC_MCR_IRQ_EN_MBOX(0));712#endif713714if (bcm_lintc_pic_attach(sc) != 0) {715device_printf(dev, "could not attach PIC\n");716return (ENXIO);717}718719BCM_LINTC_LOCK_INIT(sc);720bcm_lintc_sc = sc;721return (0);722}723724static device_method_t bcm_lintc_methods[] = {725DEVMETHOD(device_probe, bcm_lintc_probe),726DEVMETHOD(device_attach, bcm_lintc_attach),727728DEVMETHOD(pic_disable_intr, bcm_lintc_disable_intr),729DEVMETHOD(pic_enable_intr, bcm_lintc_enable_intr),730DEVMETHOD(pic_map_intr, bcm_lintc_map_intr),731DEVMETHOD(pic_post_filter, bcm_lintc_post_filter),732DEVMETHOD(pic_post_ithread, bcm_lintc_post_ithread),733DEVMETHOD(pic_pre_ithread, bcm_lintc_pre_ithread),734DEVMETHOD(pic_setup_intr, bcm_lintc_setup_intr),735#ifdef SMP736DEVMETHOD(pic_init_secondary, bcm_lintc_init_secondary),737DEVMETHOD(pic_ipi_send, bcm_lintc_ipi_send),738DEVMETHOD(pic_ipi_setup, bcm_lintc_ipi_setup),739#endif740741DEVMETHOD_END742};743744static driver_t bcm_lintc_driver = {745"lintc",746bcm_lintc_methods,747sizeof(struct bcm_lintc_softc),748};749750EARLY_DRIVER_MODULE(lintc, simplebus, bcm_lintc_driver, 0, 0,751BUS_PASS_INTERRUPT);752753754