Date: Thu, 12 Dec 2019 19:21:16 +0000 (UTC) From: Kyle Evans <kevans@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org Subject: svn commit: r355665 - in stable/12/sys: arm/broadcom/bcm2835 arm64/conf conf Message-ID: <201912121921.xBCJLGSQ095280@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: kevans Date: Thu Dec 12 19:21:16 2019 New Revision: 355665 URL: https://svnweb.freebsd.org/changeset/base/355665 Log: MFC Collection of Raspberry Pi fixes: r348803-r348804, r354488, r354524, r354560-r354561, r354563, r354577, r354579, r354823, r354825, r354844-r354846, r354868, r354875-r354876, r354930-r354933, r354956, r355016, r355025-r355026, r355031, r355563 r348803 by bz: bcm2835_sdhci.c: save block registers to avoid controller bug Extending what the initial revision, r273264, r276985, r277346 have started for the transfer mode and command registers, another pair of 16bit registers written in sequence are block size and block count, which fall together onto the same 32bit line and hence the same register(s) would be written twice in sequence for those as well. Use a similar approach to transfer mode and command and save the writes to either of the block regiters and then only execute a write once. We can do this as with transfer mode their values are meaningless until a command is issued so we can use that write to command as a trigger to also write out the block registers. Compared to transfer mode and command the value of block count can change, so we need to keep state and actually read the block registers back the first time after a write. r348804 by bz: bcm2835_sdhci.c: exit DMA if not enough data left to avoid timeout errors In the DMA case, given we disable the data interrupts, we never seem to get DATA_END. Given we are relying on DMA interrupts we are not using the SDHCI state machine and hence only call into sdhci_platform_will_handle() for the first check of data. We do not call "will handle" for any following round trips of the same transaction if block size * count > BCM_DMA_BLOCK_SIZE. Manually check "left" in the DMA interrupt handler to see if we have at least another full BCM_DMA_BLOCK_SIZE to handle. Without this change we would DMA that and then even start a DMA with left == 0 which would lead to a timeout and error. Now we re-enable data interrupts and return and let the SDHCI generic interrupt handler and state machine pick the SPACE_AVAIL up and then find that it should punt to the pio_handler for the remaining bytes or finish the data transaction. With this change block mode seems to work beyond 7 * 64byte blocks, which worked as it was below BCM_DMA_BLOCK_SIZE. r354488: bcm_lintc: don't attach if "interrupt-controller" is missing This is a standard required property for interrupt controllers, and present on the bcm_lintc nodes for currently supported RPi models. For the RPi4, we have both bcm_lintc as well as GIC-400, but only one may be active at a time. Don't probe bcm_lintc if it's missing the "interrupt-controller" property -- in RPi 4 DTS, the bcm_lintc node is actually missing this along with other required interrupt properties. Presumably, if the earlier boot stages will support switching to the legacy interrupt controller (as is suggested possible by the documentation), the DTS will need to be updated to indicate the proper interrupt-parent and hopefully also mark this node as an interrupt-controller instead. r354524: bcm2835_dma: Mark IRQs shareable On the RPi4, some of these IRQs are shared. Start moving toward a mode where we accept that shared IRQs happen and simply ignore interrupts that are seemingly for no reason. I would like to be more verbose here, but my 30-minute assessment of the current world order is that mapping a resource/rid to an actual IRQ number (as found in FDT) data is not a simple matter. Determining if more than one handler is attached to an IRQ is closer to feasible, but it's unclear which way is the cleaner path. Beyond that, we're only really using it to be slightly more verbose when something's going wrong, so for now just suppress and drop a complaint-comment. This was originally submitted (via freebsd-arm@) by Robert Crowston; the additional verbosity was dropped by kevans@. r354560: bcm2835_sdhci: add some very basic support for rpi4 DMA is currently disabled while I work out why it's broken, but this is enough for upstream U-Boot + rpi-firmware + our rpi3-psci-monitor to boot with the right config. The RPi 4 is still not in a good "supported" state, as we have no USB/PCI-E/Ethernet drivers, but if air-gapped pies only able to operate over cereal is your thing, here's your guy. r354561: bcm2835_sdhci: remove unused power_id field This was once set, but I removed it by the time I committed it because both configurations use the same POWER_ID. This can be separated back out if the situation changes. r354563: bcm2835: commit missing constant from r354560 Surgically pulling the patch from my debugging work lead to this slopiness- my apologies. r354577: arm64: add SOC_BRCM_BCM2838, build it in GENERIC BCM2838/BCM2711 is the Raspberry Pi 4, which we will soon be able to boot on once some ports bits are worked out. r354579: bcm2835_sdhci: don't panic in DMA interrupt if curcmd went away This is an exceptional case; generally found during controller errors. A panic when we attempt to acess slot->curcmd->data is less ideal than warning, and other verbiage will be emitted to indicate the exact error. r354823: bcm2835_sdhci: push DATA_END handling out of DMA interrupt path This simplifies the DMA interrupt handler quite a bit. The sdhci framework will call platform_finish_transfer() if it's received SDHCI_INT_DATA_END, so we can take care of any final cleanup there and simply not worry about the possibility of it ending in the DMA interrupt path. r354825: bcm2835_sdhci: use a macro for interrupts we handle This is just further simplification, very little functional change. In the DMA interrupt handler, we *do* now acknowledge both DATA_AVAIL | SPACE_AVAIL every time -- these operations are mutually exclusive, so while this is a functional change, it's effectively a nop. Removing the 'mask' local allows us to further simplify in a future change. r354844: bcm2835_sdhci: drop an assert in start_dma_seg Trivial change to clarify locking expectations... no functional change. r354845: bcm2835_sdhci: some style cleanup, no functional change r354846: bcm2835_sdhci: formalize DMA tag/segment scaling requirements This allows easy and care-free scaling of NUM_DMA_SEGS with proper-ish calculations to make sure we can actually handle the number of segments we'd like to handle on average so that performance comparisons can be easily made at different values if/once we can actually handle it. It also makes it helps the untrained reader understand more quickly the reasoning behind the choice of maxsize/maxsegs/maxsegsize. r354868: bcm2835_sdhci: various refactoring of DMA path This round of refactoring is mostly about streamlining the interrupt handler to make it easier to verify and reason about operations taking place while trying to bring FreeBSD up on the RPi4. r354875: bcm2835: push address mapping conversion for DMA/mailbox to runtime We could maintain the static conversions for the !AArch64 Raspberry Pis, but I'm not sure it's worth it -- we'll traverse the platform list exactly once (of which there are only two for armv7), then every conversion there-after traverses the memory map listing of which there are at-most two entries for these boards: sdram and peripheral space. Detecting this at runtime is necessary for the AArch64 SOC, though, because of the distinct IO windows being otherwise not discernible just from support compiled into the kernel. We currently select the correct window based on /compatible in the FDT. We also use a similar mechanism to describe the DMA restrictions- the RPi 4 can have up to 4GB of RAM while the DMA controller and mailbox mechanism can technically, kind of, only access the lowest 1GB. See the comment in bcm2835_vcbus.h for a fun description/clarification of this. r354876: bcm2835_vcbus: add compatibility name for ^/sys/contrib/vchiq It's unclear how this didn't get caught in my last iteration, but the fix is easy- the interface is still compatible, it was just gratuituously renamed to match my arbitrary definition of consistency... VCBUS, the BCM2835 name, represents an address on the VideoCore CPU Bus. In a similar fashion, while it is a physical address, the ARMC portion represents that these are addresses as seen by the ARM CPU. To make things even more fun, the BCM2711 peripheral documentation describes not virtual address space vs. physical address space, but instead the 32-bit address map vs. the address map in "Low Peripheral" mode. The latter of these is what the *ARMC* macros translate to/from. r354930: bcm2835_sdhci: clean up DMA segments in error handling path Later parts assume that this would've been done if interrupts are enabled, but this is the only case in which that wouldn't have been true. This commit also reorders operations such that we're done touching slot/slot->intmask before we call back into the SDHCI framework and exit. r354931: Revert r354930: wrong diff, right message. r354932: bcm2835_sdhci: roll back r354823 r354823 kicked DATA_END handling out of the DMA interrupt path "to make things easy", but this was likely a mistake -- if we know we're done after we've finished pending DMA operations, we should go ahead and acknowledge it rather than waiting for the controller to finalize it. If it's not ready, we'll simply re-enable interrupts and wait for it anyways, to be re-entered in sdhci_data_intr. r354933: bcm2835_sdhci: clean up DMA segments in error handling path Later parts assume that this would've been done if interrupts are enabled, but this is the only case in which that wouldn't have been true. This commit also reorders operations such that we're done touching slot/slot->intmask before we call back into the SDHCI framework and exit. r354956: bcm2835_sdhci: only inspect interrupts we handle We'll write the value we read back to ack pending interrupts, but we should at least make it clear to ourselves that we only want to ack pending transfer interrupts. r355016: bcm2835_vcbus: add the *other* rpi4 compat string The DTS I used initially had brcm,bcm2838; the new one uses brcm,bcm2711. Add that one as well. r355025: bcm2835_sdhci: "fix" DMA on the RPi 4 According to the documentation I have, DREQ pacing should be required here. The DREQ# hasn't changed since the BCM2835. As soon as we attempt to setup DREQ, DMA stalls and there's no clear reason why as of yet. Setting this back to NONE seems to work just as well, though it's yet to be determined if this is a sustainable model in high-throughput scenarios. r355026: bcm2835_dma: rip out the "use_dma" flag, make it non-optional Now that it works for the Raspberry Pi 4, we can discontinue our workarounds that were put in place to at least get a bootable kernel for other testing. r355031: bcm2835_sdhci: fix non-INVARIANTS build sc is now only used to make sure we're not re-entering the data handling path erroneously. r355563: RPI: Fix DMA/SDHCI on the BCM2836 (Raspberry Pi 2) r354875 pushed VCBUS <-> ARMC translations to runtime determination, but incorrectly mapped addresses for the BCM2836 -- SOC_BCM2835 and SOC_BCM2836 are actually mutually exclusive, so the BCM2836 config (GENERIC) would have taken the latter path in the header and used 0x3f000000 as peripheral start. Easily fixed -- split out the BCM2836 into its own memmap config and use that instead if SOC_BCM2836 is included. With this, we get back to userland again. Added: stable/12/sys/arm/broadcom/bcm2835/bcm2835_vcbus.c - copied, changed from r354876, head/sys/arm/broadcom/bcm2835/bcm2835_vcbus.c Modified: stable/12/sys/arm/broadcom/bcm2835/bcm2835_dma.c stable/12/sys/arm/broadcom/bcm2835/bcm2835_dma.h stable/12/sys/arm/broadcom/bcm2835/bcm2835_ft5406.c stable/12/sys/arm/broadcom/bcm2835/bcm2835_mbox.c stable/12/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h stable/12/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c stable/12/sys/arm/broadcom/bcm2835/bcm2835_vcbus.h stable/12/sys/arm/broadcom/bcm2835/bcm2836.c stable/12/sys/arm/broadcom/bcm2835/files.bcm283x stable/12/sys/arm64/conf/GENERIC stable/12/sys/conf/files.arm64 stable/12/sys/conf/options.arm64 Directory Properties: stable/12/ (props changed) Modified: stable/12/sys/arm/broadcom/bcm2835/bcm2835_dma.c ============================================================================== --- stable/12/sys/arm/broadcom/bcm2835/bcm2835_dma.c Thu Dec 12 19:17:30 2019 (r355664) +++ stable/12/sys/arm/broadcom/bcm2835/bcm2835_dma.c Thu Dec 12 19:21:16 2019 (r355665) @@ -169,7 +169,7 @@ bcm_dmamap_cb(void *arg, bus_dma_segment_t *segs, return; addr = (bus_addr_t*)arg; - *addr = PHYS_TO_VCBUS(segs[0].ds_addr); + *addr = ARMC_TO_VCBUS(segs[0].ds_addr); } static void @@ -247,8 +247,12 @@ bcm_dma_init(device_t dev) if ((reg & bcm_dma_channel_mask) != 0) device_printf(dev, "statuses are not cleared\n"); - /* Allocate DMA chunks control blocks */ - /* p.40 of spec - control block should be 32-bit aligned */ + /* + * Allocate DMA chunks control blocks based on p.40 of the peripheral + * spec - control block should be 32-bit aligned. The DMA controller + * has a full 32-bit register dedicated to this address, so we do not + * need to bother with the per-SoC peripheral restrictions. + */ err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, @@ -561,14 +565,9 @@ bcm_dma_start(int ch, vm_paddr_t src, vm_paddr_t dst, return (-1); cb = sc->sc_dma_ch[ch].cb; - if (BCM2835_ARM_IS_IO(src)) - cb->src = IO_TO_VCBUS(src); - else - cb->src = PHYS_TO_VCBUS(src); - if (BCM2835_ARM_IS_IO(dst)) - cb->dst = IO_TO_VCBUS(dst); - else - cb->dst = PHYS_TO_VCBUS(dst); + cb->src = ARMC_TO_VCBUS(src); + cb->dst = ARMC_TO_VCBUS(dst); + cb->len = len; bus_dmamap_sync(sc->sc_dma_tag, @@ -619,18 +618,18 @@ bcm_dma_intr(void *arg) /* my interrupt? */ cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch->ch)); - if (!(cs & (CS_INT | CS_ERR))) { - device_printf(sc->sc_dev, - "unexpected DMA intr CH=%d, CS=%x\n", ch->ch, cs); + /* + * Is it an active channel? Our diagnostics could be better here, but + * it's not necessarily an easy task to resolve a rid/resource to an + * actual irq number. We'd want to do this to set a flag indicating + * whether the irq is shared or not, so we know to complain. + */ + if (!(ch->flags & BCM_DMA_CH_USED)) return; - } - /* running? */ - if (!(ch->flags & BCM_DMA_CH_USED)) { - device_printf(sc->sc_dev, - "unused DMA intr CH=%d, CS=%x\n", ch->ch, cs); + /* Again, we can't complain here. The same logic applies. */ + if (!(cs & (CS_INT | CS_ERR))) return; - } if (cs & CS_ERR) { debug = bus_read_4(sc->sc_mem, BCM_DMA_DEBUG(ch->ch)); @@ -715,7 +714,7 @@ bcm_dma_attach(device_t dev) continue; sc->sc_irq[rid] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, - RF_ACTIVE); + RF_ACTIVE | RF_SHAREABLE); if (sc->sc_irq[rid] == NULL) { device_printf(dev, "cannot allocate interrupt\n"); err = ENXIO; Modified: stable/12/sys/arm/broadcom/bcm2835/bcm2835_dma.h ============================================================================== --- stable/12/sys/arm/broadcom/bcm2835/bcm2835_dma.h Thu Dec 12 19:17:30 2019 (r355664) +++ stable/12/sys/arm/broadcom/bcm2835/bcm2835_dma.h Thu Dec 12 19:21:16 2019 (r355665) @@ -42,6 +42,10 @@ /* Peripheral DREQ Signals (4.2.1.3) */ #define BCM_DMA_DREQ_NONE 0 +/* + * XXX This doesn't seem to work for the Raspberry Pi 4, but the peripheral + * documentation still lists it at 11. + */ #define BCM_DMA_DREQ_EMMC 11 #define BCM_DMA_DREQ_SDHOST 13 Modified: stable/12/sys/arm/broadcom/bcm2835/bcm2835_ft5406.c ============================================================================== --- stable/12/sys/arm/broadcom/bcm2835/bcm2835_ft5406.c Thu Dec 12 19:17:30 2019 (r355664) +++ stable/12/sys/arm/broadcom/bcm2835/bcm2835_ft5406.c Thu Dec 12 19:21:16 2019 (r355665) @@ -227,7 +227,7 @@ ft5406ts_init(void *arg) return; } - touchbuf = VCBUS_TO_PHYS(msg.body.resp.address); + touchbuf = VCBUS_TO_ARMC(msg.body.resp.address); sc->touch_buf = (uint8_t*)pmap_mapdev(touchbuf, FT5406_WINDOW_SIZE); /* 60Hz */ Modified: stable/12/sys/arm/broadcom/bcm2835/bcm2835_mbox.c ============================================================================== --- stable/12/sys/arm/broadcom/bcm2835/bcm2835_mbox.c Thu Dec 12 19:17:30 2019 (r355664) +++ stable/12/sys/arm/broadcom/bcm2835/bcm2835_mbox.c Thu Dec 12 19:21:16 2019 (r355665) @@ -303,7 +303,7 @@ bcm2835_mbox_dma_cb(void *arg, bus_dma_segment_t *segs if (err) return; addr = (bus_addr_t *)arg; - *addr = PHYS_TO_VCBUS(segs[0].ds_addr); + *addr = ARMC_TO_VCBUS(segs[0].ds_addr); } static void * @@ -314,7 +314,7 @@ bcm2835_mbox_init_dma(device_t dev, size_t len, bus_dm int err; err = bus_dma_tag_create(bus_get_dma_tag(dev), 16, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + bcm283x_dmabus_peripheral_lowaddr(), BUS_SPACE_MAXADDR, NULL, NULL, len, 1, len, 0, NULL, NULL, tag); if (err != 0) { device_printf(dev, "can't create DMA tag\n"); @@ -534,7 +534,7 @@ bcm2835_mbox_fb_init(struct bcm2835_fb_config *fb) fb->xoffset = msg.offset.body.resp.x; fb->yoffset = msg.offset.body.resp.y; fb->pitch = msg.pitch.body.resp.pitch; - fb->base = VCBUS_TO_PHYS(msg.buffer.body.resp.fb_address); + fb->base = VCBUS_TO_ARMC(msg.buffer.body.resp.fb_address); fb->size = msg.buffer.body.resp.fb_size; } Modified: stable/12/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h ============================================================================== --- stable/12/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h Thu Dec 12 19:17:30 2019 (r355664) +++ stable/12/sys/arm/broadcom/bcm2835/bcm2835_mbox_prop.h Thu Dec 12 19:21:16 2019 (r355665) @@ -118,6 +118,7 @@ int bcm2835_mbox_set_power_state(uint32_t, boolean_t); #define BCM2835_MBOX_CLOCK_ID_SDRAM 0x00000008 #define BCM2835_MBOX_CLOCK_ID_PIXEL 0x00000009 #define BCM2835_MBOX_CLOCK_ID_PWM 0x0000000a +#define BCM2838_MBOX_CLOCK_ID_EMMC2 0x0000000c #define BCM2835_MBOX_TAG_GET_CLOCK_RATE 0x00030002 #define BCM2835_MBOX_TAG_SET_CLOCK_RATE 0x00038002 Modified: stable/12/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c ============================================================================== --- stable/12/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c Thu Dec 12 19:17:30 2019 (r355664) +++ stable/12/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c Thu Dec 12 19:21:16 2019 (r355665) @@ -58,13 +58,37 @@ __FBSDID("$FreeBSD$"); #include "bcm2835_dma.h" #include <arm/broadcom/bcm2835/bcm2835_mbox_prop.h> -#include "bcm2835_vcbus.h" +#ifdef NOTYET +#include <arm/broadcom/bcm2835/bcm2835_clkman.h> +#endif +#include <arm/broadcom/bcm2835/bcm2835_vcbus.h> #define BCM2835_DEFAULT_SDHCI_FREQ 50 +#define BCM2838_DEFAULT_SDHCI_FREQ 100 #define BCM_SDHCI_BUFFER_SIZE 512 -#define NUM_DMA_SEGS 2 +/* + * NUM_DMA_SEGS is the number of DMA segments we want to accommodate on average. + * We add in a number of segments based on how much we may need to spill into + * another segment due to crossing page boundaries. e.g. up to PAGE_SIZE, an + * extra page is needed as we can cross a page boundary exactly once. + */ +#define NUM_DMA_SEGS 1 +#define NUM_DMA_SPILL_SEGS \ + ((((NUM_DMA_SEGS * BCM_SDHCI_BUFFER_SIZE) - 1) / PAGE_SIZE) + 1) +#define ALLOCATED_DMA_SEGS (NUM_DMA_SEGS + NUM_DMA_SPILL_SEGS) +#define BCM_DMA_MAXSIZE (NUM_DMA_SEGS * BCM_SDHCI_BUFFER_SIZE) +#define BCM_SDHCI_SLOT_LEFT(slot) \ + ((slot)->curcmd->data->len - (slot)->offset) + +#define BCM_SDHCI_SEGSZ_LEFT(slot) \ + min(BCM_DMA_MAXSIZE, \ + rounddown(BCM_SDHCI_SLOT_LEFT(slot), BCM_SDHCI_BUFFER_SIZE)) + +#define DATA_PENDING_MASK (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL) +#define DATA_XFER_MASK (DATA_PENDING_MASK | SDHCI_INT_DATA_END) + #ifdef DEBUG static int bcm2835_sdhci_debug = 0; @@ -84,10 +108,38 @@ SYSCTL_INT(_hw_sdhci, OID_AUTO, bcm2835_sdhci_debug, C static int bcm2835_sdhci_hs = 1; static int bcm2835_sdhci_pio_mode = 0; +struct bcm_mmc_conf { + int clock_id; + int clock_src; + int default_freq; + int quirks; + int emmc_dreq; +}; + +struct bcm_mmc_conf bcm2835_sdhci_conf = { + .clock_id = BCM2835_MBOX_CLOCK_ID_EMMC, + .clock_src = -1, + .default_freq = BCM2835_DEFAULT_SDHCI_FREQ, + .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DONT_SET_HISPD_BIT | + SDHCI_QUIRK_MISSING_CAPS, + .emmc_dreq = BCM_DMA_DREQ_EMMC, +}; + +struct bcm_mmc_conf bcm2838_emmc2_conf = { + .clock_id = BCM2838_MBOX_CLOCK_ID_EMMC2, + .clock_src = -1, + .default_freq = BCM2838_DEFAULT_SDHCI_FREQ, + .quirks = 0, + .emmc_dreq = BCM_DMA_DREQ_NONE, +}; + static struct ofw_compat_data compat_data[] = { - {"broadcom,bcm2835-sdhci", 1}, - {"brcm,bcm2835-sdhci", 1}, - {"brcm,bcm2835-mmc", 1}, + {"broadcom,bcm2835-sdhci", (uintptr_t)&bcm2835_sdhci_conf}, + {"brcm,bcm2835-sdhci", (uintptr_t)&bcm2835_sdhci_conf}, + {"brcm,bcm2835-mmc", (uintptr_t)&bcm2835_sdhci_conf}, + {"brcm,bcm2711-emmc2", (uintptr_t)&bcm2838_emmc2_conf}, + {"brcm,bcm2838-emmc2", (uintptr_t)&bcm2838_emmc2_conf}, {NULL, 0} }; @@ -107,12 +159,18 @@ struct bcm_sdhci_softc { bus_dma_tag_t sc_dma_tag; bus_dmamap_t sc_dma_map; vm_paddr_t sc_sdhci_buffer_phys; - uint32_t cmd_and_mode; - bus_addr_t dmamap_seg_addrs[NUM_DMA_SEGS]; - bus_size_t dmamap_seg_sizes[NUM_DMA_SEGS]; + bus_addr_t dmamap_seg_addrs[ALLOCATED_DMA_SEGS]; + bus_size_t dmamap_seg_sizes[ALLOCATED_DMA_SEGS]; int dmamap_seg_count; int dmamap_seg_index; int dmamap_status; + uint32_t blksz_and_count; + uint32_t cmd_and_mode; + bool need_update_blk; +#ifdef NOTYET + device_t clkman; +#endif + struct bcm_mmc_conf * conf; }; static int bcm_sdhci_probe(device_t); @@ -122,6 +180,7 @@ static void bcm_sdhci_intr(void *); static int bcm_sdhci_get_ro(device_t, device_t); static void bcm_sdhci_dma_intr(int ch, void *arg); +static void bcm_sdhci_start_dma(struct sdhci_slot *slot); static void bcm_sdhci_dmacb(void *arg, bus_dma_segment_t *segs, int nseg, int err) @@ -129,6 +188,8 @@ bcm_sdhci_dmacb(void *arg, bus_dma_segment_t *segs, in struct bcm_sdhci_softc *sc = arg; int i; + /* Sanity check: we can only ever have one mapping at a time. */ + KASSERT(sc->dmamap_seg_count == 0, ("leaked DMA segment")); sc->dmamap_status = err; sc->dmamap_seg_count = nseg; @@ -166,8 +227,12 @@ bcm_sdhci_attach(device_t dev) sc->sc_dev = dev; sc->sc_req = NULL; - err = bcm2835_mbox_set_power_state(BCM2835_MBOX_POWER_ID_EMMC, - TRUE); + sc->conf = (struct bcm_mmc_conf *)ofw_bus_search_compatible(dev, + compat_data)->ocd_data; + if (sc->conf == 0) + return (ENXIO); + + err = bcm2835_mbox_set_power_state(BCM2835_MBOX_POWER_ID_EMMC, TRUE); if (err != 0) { if (bootverbose) device_printf(dev, "Unable to enable the power\n"); @@ -175,8 +240,7 @@ bcm_sdhci_attach(device_t dev) } default_freq = 0; - err = bcm2835_mbox_get_clock_rate(BCM2835_MBOX_CLOCK_ID_EMMC, - &default_freq); + err = bcm2835_mbox_get_clock_rate(sc->conf->clock_id, &default_freq); if (err == 0) { /* Convert to MHz */ default_freq /= 1000000; @@ -188,11 +252,31 @@ bcm_sdhci_attach(device_t dev) default_freq = cell / 1000000; } if (default_freq == 0) - default_freq = BCM2835_DEFAULT_SDHCI_FREQ; + default_freq = sc->conf->default_freq; if (bootverbose) device_printf(dev, "SDHCI frequency: %dMHz\n", default_freq); +#ifdef NOTYET + if (sc->conf->clock_src > 0) { + uint32_t f; + sc->clkman = devclass_get_device( + devclass_find("bcm2835_clkman"), 0); + if (sc->clkman == NULL) { + device_printf(dev, "cannot find Clock Manager\n"); + return (ENXIO); + } + f = bcm2835_clkman_set_frequency(sc->clkman, + sc->conf->clock_src, default_freq); + if (f == 0) + return (EINVAL); + + if (bootverbose) + device_printf(dev, "Clock source frequency: %dMHz\n", + f); + } +#endif + rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); @@ -207,7 +291,7 @@ bcm_sdhci_attach(device_t dev) rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, - RF_ACTIVE); + RF_ACTIVE | RF_SHAREABLE); if (!sc->sc_irq_res) { device_printf(dev, "cannot allocate interrupt\n"); err = ENXIO; @@ -228,24 +312,27 @@ bcm_sdhci_attach(device_t dev) if (bcm2835_sdhci_hs) sc->sc_slot.caps |= SDHCI_CAN_DO_HISPD; sc->sc_slot.caps |= (default_freq << SDHCI_CLOCK_BASE_SHIFT); - sc->sc_slot.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK - | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL - | SDHCI_QUIRK_DONT_SET_HISPD_BIT - | SDHCI_QUIRK_MISSING_CAPS; - + sc->sc_slot.quirks = sc->conf->quirks; + sdhci_init_slot(dev, &sc->sc_slot, 0); sc->sc_dma_ch = bcm_dma_allocate(BCM_DMA_CH_ANY); if (sc->sc_dma_ch == BCM_DMA_CH_INVALID) goto fail; - bcm_dma_setup_intr(sc->sc_dma_ch, bcm_sdhci_dma_intr, sc); + err = bcm_dma_setup_intr(sc->sc_dma_ch, bcm_sdhci_dma_intr, sc); + if (err != 0) { + device_printf(dev, + "cannot setup dma interrupt handler\n"); + err = ENXIO; + goto fail; + } /* Allocate bus_dma resources. */ err = bus_dma_tag_create(bus_get_dma_tag(dev), - 1, 0, BUS_SPACE_MAXADDR_32BIT, + 1, 0, bcm283x_dmabus_peripheral_lowaddr(), BUS_SPACE_MAXADDR, NULL, NULL, - BCM_SDHCI_BUFFER_SIZE, NUM_DMA_SEGS, BCM_SDHCI_BUFFER_SIZE, + BCM_DMA_MAXSIZE, ALLOCATED_DMA_SEGS, BCM_SDHCI_BUFFER_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_dma_tag); @@ -269,6 +356,10 @@ bcm_sdhci_attach(device_t dev) sdhci_start_slot(&sc->sc_slot); + /* Seed our copies. */ + sc->blksz_and_count = SDHCI_READ_4(dev, &sc->sc_slot, SDHCI_BLOCK_SIZE); + sc->cmd_and_mode = SDHCI_READ_4(dev, &sc->sc_slot, SDHCI_TRANSFER_MODE); + return (0); fail: @@ -319,7 +410,7 @@ WR4(struct bcm_sdhci_softc *sc, bus_size_t off, uint32 /* * The Arasan HC has a bug where it may lose the content of * consecutive writes to registers that are within two SD-card - * clock cycles of each other (a clock domain crossing problem). + * clock cycles of each other (a clock domain crossing problem). */ if (sc->sc_slot.clock > 0) DELAY(((2 * 1000000) / sc->sc_slot.clock) + 1); @@ -338,17 +429,21 @@ static uint16_t bcm_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhci_softc *sc = device_get_softc(dev); - uint32_t val = RD4(sc, off & ~3); + uint32_t val32; /* - * Standard 32-bit handling of command and transfer mode. + * Standard 32-bit handling of command and transfer mode, as + * well as block size and count. */ - if (off == SDHCI_TRANSFER_MODE) { - return (sc->cmd_and_mode >> 16); - } else if (off == SDHCI_COMMAND_FLAGS) { - return (sc->cmd_and_mode & 0x0000ffff); - } - return ((val >> (off & 3)*8) & 0xffff); + if ((off == SDHCI_BLOCK_SIZE || off == SDHCI_BLOCK_COUNT) && + sc->need_update_blk) + val32 = sc->blksz_and_count; + else if (off == SDHCI_TRANSFER_MODE || off == SDHCI_COMMAND_FLAGS) + val32 = sc->cmd_and_mode; + else + val32 = RD4(sc, off & ~3); + + return ((val32 >> (off & 3)*8) & 0xffff); } static uint32_t @@ -369,7 +464,8 @@ bcm_sdhci_read_multi_4(device_t dev, struct sdhci_slot } static void -bcm_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val) +bcm_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint8_t val) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val32 = RD4(sc, off & ~3); @@ -379,27 +475,54 @@ bcm_sdhci_write_1(device_t dev, struct sdhci_slot *slo } static void -bcm_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val) +bcm_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint16_t val) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val32; - if (off == SDHCI_COMMAND_FLAGS) + + /* + * If we have a queued up 16bit value for blk size or count, use and + * update the saved value rather than doing any real register access. + * If we did not touch either since the last write, then read from + * register as at least block count can change. + * Similarly, if we are about to issue a command, always use the saved + * value for transfer mode as we can never write that without issuing + * a command. + */ + if ((off == SDHCI_BLOCK_SIZE || off == SDHCI_BLOCK_COUNT) && + sc->need_update_blk) + val32 = sc->blksz_and_count; + else if (off == SDHCI_COMMAND_FLAGS) val32 = sc->cmd_and_mode; else val32 = RD4(sc, off & ~3); + val32 &= ~(0xffff << (off & 3)*8); val32 |= (val << (off & 3)*8); + if (off == SDHCI_TRANSFER_MODE) sc->cmd_and_mode = val32; - else { - WR4(sc, off & ~3, val32); - if (off == SDHCI_COMMAND_FLAGS) + else if (off == SDHCI_BLOCK_SIZE || off == SDHCI_BLOCK_COUNT) { + sc->blksz_and_count = val32; + sc->need_update_blk = true; + } else { + if (off == SDHCI_COMMAND_FLAGS) { + /* If we saved blk writes, do them now before cmd. */ + if (sc->need_update_blk) { + WR4(sc, SDHCI_BLOCK_SIZE, sc->blksz_and_count); + sc->need_update_blk = false; + } + /* Always save cmd and mode registers. */ sc->cmd_and_mode = val32; + } + WR4(sc, off & ~3, val32); } } static void -bcm_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val) +bcm_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint32_t val) { struct bcm_sdhci_softc *sc = device_get_softc(dev); WR4(sc, off, val); @@ -419,27 +542,35 @@ bcm_sdhci_start_dma_seg(struct bcm_sdhci_softc *sc) { struct sdhci_slot *slot; vm_paddr_t pdst, psrc; - int err, idx, len, sync_op; + int err, idx, len, sync_op, width; slot = &sc->sc_slot; + mtx_assert(&slot->mtx, MA_OWNED); idx = sc->dmamap_seg_index++; len = sc->dmamap_seg_sizes[idx]; slot->offset += len; + width = (len & 0xf ? BCM_DMA_32BIT : BCM_DMA_128BIT); if (slot->curcmd->data->flags & MMC_DATA_READ) { - bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC, - BCM_DMA_SAME_ADDR, BCM_DMA_32BIT); + /* + * Peripherals on the AXI bus do not need DREQ pacing for reads + * from the ARM core, so we can safely set this to NONE. + */ + bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE, + BCM_DMA_SAME_ADDR, BCM_DMA_32BIT); bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_NONE, - BCM_DMA_INC_ADDR, - (len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT); + BCM_DMA_INC_ADDR, width); psrc = sc->sc_sdhci_buffer_phys; pdst = sc->dmamap_seg_addrs[idx]; sync_op = BUS_DMASYNC_PREREAD; } else { + /* + * The ordering here is important, because the last write to + * dst/src in the dma control block writes the real dreq value. + */ bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE, - BCM_DMA_INC_ADDR, - (len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT); - bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC, + BCM_DMA_INC_ADDR, width); + bcm_dma_setup_dst(sc->sc_dma_ch, sc->conf->emmc_dreq, BCM_DMA_SAME_ADDR, BCM_DMA_32BIT); psrc = sc->dmamap_seg_addrs[idx]; pdst = sc->sc_sdhci_buffer_phys; @@ -453,9 +584,9 @@ bcm_sdhci_start_dma_seg(struct bcm_sdhci_softc *sc) */ if (idx == 0) { bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op); - slot->intmask &= ~(SDHCI_INT_DATA_AVAIL | - SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END); - bcm_sdhci_write_4(sc->sc_dev, &sc->sc_slot, SDHCI_SIGNAL_ENABLE, + + slot->intmask &= ~DATA_XFER_MASK; + bcm_sdhci_write_4(sc->sc_dev, slot, SDHCI_SIGNAL_ENABLE, slot->intmask); } @@ -468,15 +599,47 @@ bcm_sdhci_start_dma_seg(struct bcm_sdhci_softc *sc) } static void +bcm_sdhci_dma_exit(struct bcm_sdhci_softc *sc) +{ + struct sdhci_slot *slot = &sc->sc_slot; + + mtx_assert(&slot->mtx, MA_OWNED); + + /* Re-enable interrupts */ + slot->intmask |= DATA_XFER_MASK; + bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE, + slot->intmask); +} + +static void +bcm_sdhci_dma_unload(struct bcm_sdhci_softc *sc) +{ + struct sdhci_slot *slot = &sc->sc_slot; + + if (sc->dmamap_seg_count == 0) + return; + if ((slot->curcmd->data->flags & MMC_DATA_READ) != 0) + bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, + BUS_DMASYNC_POSTREAD); + else + bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dma_tag, sc->sc_dma_map); + + sc->dmamap_seg_count = 0; + sc->dmamap_seg_index = 0; +} + +static void bcm_sdhci_dma_intr(int ch, void *arg) { struct bcm_sdhci_softc *sc = (struct bcm_sdhci_softc *)arg; struct sdhci_slot *slot = &sc->sc_slot; - uint32_t reg, mask; - int left, sync_op; + uint32_t reg; mtx_lock(&slot->mtx); - + if (slot->curcmd == NULL) + goto out; /* * If there are more segments for the current dma, start the next one. * Otherwise unload the dma map and decide what to do next based on the @@ -484,93 +647,64 @@ bcm_sdhci_dma_intr(int ch, void *arg) */ if (sc->dmamap_seg_index < sc->dmamap_seg_count) { bcm_sdhci_start_dma_seg(sc); - mtx_unlock(&slot->mtx); - return; + goto out; } - if (slot->curcmd->data->flags & MMC_DATA_READ) { - sync_op = BUS_DMASYNC_POSTREAD; - mask = SDHCI_INT_DATA_AVAIL; - } else { - sync_op = BUS_DMASYNC_POSTWRITE; - mask = SDHCI_INT_SPACE_AVAIL; - } - bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op); - bus_dmamap_unload(sc->sc_dma_tag, sc->sc_dma_map); + bcm_sdhci_dma_unload(sc); - sc->dmamap_seg_count = 0; - sc->dmamap_seg_index = 0; + /* + * If we had no further segments pending, we need to determine how to + * proceed next. If the 'data/space pending' bit is already set and we + * can continue via DMA, do so. Otherwise, re-enable interrupts and + * return. + */ + reg = bcm_sdhci_read_4(slot->bus, slot, SDHCI_INT_STATUS) & + DATA_XFER_MASK; + if ((reg & DATA_PENDING_MASK) != 0 && + BCM_SDHCI_SEGSZ_LEFT(slot) >= BCM_SDHCI_BUFFER_SIZE) { + /* ACK any pending interrupts */ + bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS, + DATA_PENDING_MASK); - left = min(BCM_SDHCI_BUFFER_SIZE, - slot->curcmd->data->len - slot->offset); - - /* DATA END? */ - reg = bcm_sdhci_read_4(slot->bus, slot, SDHCI_INT_STATUS); - - if (reg & SDHCI_INT_DATA_END) { - /* ACK for all outstanding interrupts */ - bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS, reg); - - /* enable INT */ - slot->intmask |= SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL - | SDHCI_INT_DATA_END; - bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE, - slot->intmask); - - /* finish this data */ - sdhci_finish_data(slot); - } - else { - /* already available? */ - if (reg & mask) { - - /* ACK for DATA_AVAIL or SPACE_AVAIL */ - bcm_sdhci_write_4(slot->bus, slot, - SDHCI_INT_STATUS, mask); - - /* continue next DMA transfer */ - if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, - (uint8_t *)slot->curcmd->data->data + - slot->offset, left, bcm_sdhci_dmacb, sc, - BUS_DMA_NOWAIT) != 0 || sc->dmamap_status != 0) { - slot->curcmd->error = MMC_ERR_NO_MEMORY; - sdhci_finish_data(slot); - } else { - bcm_sdhci_start_dma_seg(sc); - } - } else { - /* wait for next data by INT */ - - /* enable INT */ - slot->intmask |= SDHCI_INT_DATA_AVAIL | - SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END; - bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE, - slot->intmask); + bcm_sdhci_start_dma(slot); + if (slot->curcmd->error != 0) { + /* We won't recover from this error for this command. */ + bcm_sdhci_dma_unload(sc); + bcm_sdhci_dma_exit(sc); + sdhci_finish_data(slot); } + } else if ((reg & SDHCI_INT_DATA_END) != 0) { + bcm_sdhci_dma_exit(sc); + bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS, + reg); + slot->flags &= ~PLATFORM_DATA_STARTED; + sdhci_finish_data(slot); + } else { + bcm_sdhci_dma_exit(sc); } - +out: mtx_unlock(&slot->mtx); } static void -bcm_sdhci_read_dma(device_t dev, struct sdhci_slot *slot) +bcm_sdhci_start_dma(struct sdhci_slot *slot) { struct bcm_sdhci_softc *sc = device_get_softc(slot->bus); + uint8_t *buf; size_t left; - if (sc->dmamap_seg_count != 0) { - device_printf(sc->sc_dev, "DMA in use\n"); - return; - } + mtx_assert(&slot->mtx, MA_OWNED); - left = min(BCM_SDHCI_BUFFER_SIZE, - slot->curcmd->data->len - slot->offset); + left = BCM_SDHCI_SEGSZ_LEFT(slot); + buf = (uint8_t *)slot->curcmd->data->data + slot->offset; + KASSERT(left != 0, + ("%s: DMA handling incorrectly indicated", __func__)); - KASSERT((left & 3) == 0, - ("%s: len = %zu, not word-aligned", __func__, left)); - - if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, - (uint8_t *)slot->curcmd->data->data + slot->offset, left, + /* + * No need to check segment count here; if we've not yet unloaded + * previous segments, we'll catch that in bcm_sdhci_dmacb. + */ + if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, buf, left, bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 || sc->dmamap_status != 0) { slot->curcmd->error = MMC_ERR_NO_MEMORY; @@ -581,50 +715,29 @@ bcm_sdhci_read_dma(device_t dev, struct sdhci_slot *sl bcm_sdhci_start_dma_seg(sc); } -static void -bcm_sdhci_write_dma(device_t dev, struct sdhci_slot *slot) -{ - struct bcm_sdhci_softc *sc = device_get_softc(slot->bus); - size_t left; - - if (sc->dmamap_seg_count != 0) { - device_printf(sc->sc_dev, "DMA in use\n"); - return; - } - - left = min(BCM_SDHCI_BUFFER_SIZE, - slot->curcmd->data->len - slot->offset); - - KASSERT((left & 3) == 0, - ("%s: len = %zu, not word-aligned", __func__, left)); - - if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, - (uint8_t *)slot->curcmd->data->data + slot->offset, left, - bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 || - sc->dmamap_status != 0) { - slot->curcmd->error = MMC_ERR_NO_MEMORY; - return; - } - - /* DMA start */ - bcm_sdhci_start_dma_seg(sc); -} - static int bcm_sdhci_will_handle_transfer(device_t dev, struct sdhci_slot *slot) { - size_t left; +#ifdef INVARIANTS + struct bcm_sdhci_softc *sc = device_get_softc(slot->bus); +#endif /* - * Do not use DMA for transfers less than block size or with a length - * that is not a multiple of four. + * This indicates that we somehow let a data interrupt slip by into the + * SDHCI framework, when it should not have. This really needs to be + * caught and fixed ASAP, as it really shouldn't happen. */ - left = min(BCM_DMA_BLOCK_SIZE, - slot->curcmd->data->len - slot->offset); - if (left < BCM_DMA_BLOCK_SIZE) + KASSERT(sc->dmamap_seg_count == 0, + ("data pending interrupt pushed through SDHCI framework")); + + /* + * Do not use DMA for transfers less than our block size. Checking + * alignment serves little benefit, as we round transfer sizes down to + * a multiple of the block size and push the transfer back to + * SDHCI-driven PIO once we're below the block size. + */ + if (BCM_SDHCI_SEGSZ_LEFT(slot) < BCM_DMA_BLOCK_SIZE) return (0); - if (left & 0x03) - return (0); return (1); } @@ -635,16 +748,30 @@ bcm_sdhci_start_transfer(device_t dev, struct sdhci_sl { /* DMA transfer FIFO 1KB */ - if (slot->curcmd->data->flags & MMC_DATA_READ) - bcm_sdhci_read_dma(dev, slot); - else - bcm_sdhci_write_dma(dev, slot); + bcm_sdhci_start_dma(slot); } static void bcm_sdhci_finish_transfer(device_t dev, struct sdhci_slot *slot) { + struct bcm_sdhci_softc *sc = device_get_softc(slot->bus); + /* + * Clean up. Interrupts are clearly enabled, because we received an + * SDHCI_INT_DATA_END to get this far -- just make sure we don't leave + * anything laying around. + */ + if (sc->dmamap_seg_count != 0) { + /* + * Our segment math should have worked out such that we would + * never finish the transfer without having used up all of the + * segments. If we haven't, that means we must have erroneously + * regressed to SDHCI-driven PIO to finish the operation and + * this is certainly caused by developer-error. + */ + bcm_sdhci_dma_unload(sc); + } + sdhci_finish_data(slot); } @@ -692,6 +819,9 @@ static driver_t bcm_sdhci_driver = { DRIVER_MODULE(sdhci_bcm, simplebus, bcm_sdhci_driver, bcm_sdhci_devclass, NULL, NULL); +#ifdef NOTYET +MODULE_DEPEND(sdhci_bcm, bcm2835_clkman, 1, 1, 1); +#endif SDHCI_DEPEND(sdhci_bcm); #ifndef MMCCAM MMC_DECLARE_BRIDGE(sdhci_bcm); Copied and modified: stable/12/sys/arm/broadcom/bcm2835/bcm2835_vcbus.c (from r354876, head/sys/arm/broadcom/bcm2835/bcm2835_vcbus.c) ============================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_vcbus.c Wed Nov 20 05:04:44 2019 (r354876, copy source) +++ stable/12/sys/arm/broadcom/bcm2835/bcm2835_vcbus.c Thu Dec 12 19:21:16 2019 (r355665) @@ -67,7 +67,7 @@ struct bcm283x_memory_mapping { vm_paddr_t vcbus_start; }; -#if defined(SOC_BCM2835) || defined(SOC_BCM2836) +#ifdef SOC_BCM2835 static struct bcm283x_memory_mapping bcm2835_memmap[] = { { /* SDRAM */ @@ -85,6 +85,24 @@ static struct bcm283x_memory_mapping bcm2835_memmap[] }; #endif +#ifdef SOC_BCM2836 +static struct bcm283x_memory_mapping bcm2836_memmap[] = { + { + /* SDRAM */ + .armc_start = 0x00000000, + .armc_size = BCM2836_ARM_IO_BASE, + .vcbus_start = BCM2836_VCBUS_SDRAM_BASE, + }, + { + /* Peripherals */ + .armc_start = BCM2836_ARM_IO_BASE, + .armc_size = BCM28XX_ARM_IO_SIZE, + .vcbus_start = BCM2836_VCBUS_IO_BASE, + }, + { 0, 0, 0 }, +}; +#endif + #ifdef SOC_BRCM_BCM2837 static struct bcm283x_memory_mapping bcm2837_memmap[] = { { @@ -142,7 +160,7 @@ static struct bcm283x_memory_soc_cfg { #endif #ifdef SOC_BCM2836 { - .memmap = bcm2835_memmap, + .memmap = bcm2836_memmap, .soc_compat = "brcm,bcm2836", .busdma_lowaddr = BUS_SPACE_MAXADDR_32BIT, }, @@ -156,6 +174,11 @@ static struct bcm283x_memory_soc_cfg { }, #endif #ifdef SOC_BRCM_BCM2838 + { + .memmap = bcm2838_memmap, + .soc_compat = "brcm,bcm2711", + .busdma_lowaddr = BCM2838_PERIPH_MAXADDR, + }, { .memmap = bcm2838_memmap, .soc_compat = "brcm,bcm2838", Modified: stable/12/sys/arm/broadcom/bcm2835/bcm2835_vcbus.h ============================================================================== --- stable/12/sys/arm/broadcom/bcm2835/bcm2835_vcbus.h Thu Dec 12 19:17:30 2019 (r355664) +++ stable/12/sys/arm/broadcom/bcm2835/bcm2835_vcbus.h Thu Dec 12 19:21:16 2019 (r355665) @@ -35,47 +35,47 @@ #ifndef _BCM2835_VCBUS_H_ #define _BCM2835_VCBUS_H_ -/* - * ARM64 define its SOC options in opt_soc.h - */ -#if defined(__aarch64__) -#include "opt_soc.h" -#endif - #define BCM2835_VCBUS_SDRAM_CACHED 0x40000000 -#define BCM2835_VCBUS_IO_BASE 0x7E000000 #define BCM2835_VCBUS_SDRAM_UNCACHED 0xC0000000 -#if defined(SOC_BCM2835) #define BCM2835_ARM_IO_BASE 0x20000000 +#define BCM2835_VCBUS_IO_BASE 0x7E000000 #define BCM2835_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_CACHED -#else -#define BCM2835_ARM_IO_BASE 0x3f000000 -#define BCM2835_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_UNCACHED -#endif -#define BCM2835_ARM_IO_SIZE 0x01000000 -/* - * Convert physical address to VC bus address. Should be used - * when submitting address over mailbox interface - */ -#define PHYS_TO_VCBUS(pa) ((pa) + BCM2835_VCBUS_SDRAM_BASE) +#define BCM2836_ARM_IO_BASE 0x3f000000 +#define BCM2836_VCBUS_IO_BASE BCM2835_VCBUS_IO_BASE +#define BCM2836_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_UNCACHED -/* Check whether pa bellong top IO window */ -#define BCM2835_ARM_IS_IO(pa) (((pa) >= BCM2835_ARM_IO_BASE) && \ - ((pa) < BCM2835_ARM_IO_BASE + BCM2835_ARM_IO_SIZE)) +#define BCM2837_ARM_IO_BASE BCM2836_ARM_IO_BASE +#define BCM2837_VCBUS_IO_BASE BCM2835_VCBUS_IO_BASE +#define BCM2837_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_UNCACHED -/* - * Convert physical address in IO space to VC bus address. - */ -#define IO_TO_VCBUS(pa) ((pa - BCM2835_ARM_IO_BASE) + \ - BCM2835_VCBUS_IO_BASE) +#define BCM2838_ARM_IO_BASE 0xfe000000 +#define BCM2838_VCBUS_IO_BASE BCM2835_VCBUS_IO_BASE +#define BCM2838_VCBUS_SDRAM_BASE BCM2835_VCBUS_SDRAM_UNCACHED /* - * Convert address from VC bus space to physical. Should be used - * when address is returned by VC over mailbox interface. e.g. - * framebuffer base + * Max allowed SDRAM mapping for most peripherals. The Raspberry Pi 4 has more + * than 1 GB of SDRAM, but only the lowest 1 GB is mapped into the "Legacy + * Master view" of the address space accessible by the DMA engine. Technically, + * we can slide this window around to whatever similarly sized range is + * convenient, but this is the most useful window given how busdma(9) works and + * that the window must be reconfigured for all channels in a given DMA engine. + * The DMA lite engine's window can be configured separately from the 30-bit DMA + * engine. */ -#define VCBUS_TO_PHYS(vca) ((vca) & ~(BCM2835_VCBUS_SDRAM_BASE)) +#define BCM2838_PERIPH_MAXADDR 0x3fffffff + +#define BCM28XX_ARM_IO_SIZE 0x01000000 + +vm_paddr_t bcm283x_armc_to_vcbus(vm_paddr_t pa); +vm_paddr_t bcm283x_vcbus_to_armc(vm_paddr_t vca); +bus_addr_t bcm283x_dmabus_peripheral_lowaddr(void); + +#define ARMC_TO_VCBUS(pa) bcm283x_armc_to_vcbus(pa) +#define VCBUS_TO_ARMC(vca) bcm283x_vcbus_to_armc(vca) + +/* Compatibility name for vchiq arm interface. */ +#define PHYS_TO_VCBUS ARMC_TO_VCBUS #endif /* _BCM2835_VCBUS_H_ */ Modified: stable/12/sys/arm/broadcom/bcm2835/bcm2836.c ============================================================================== --- stable/12/sys/arm/broadcom/bcm2835/bcm2836.c Thu Dec 12 19:17:30 2019 (r355664) +++ stable/12/sys/arm/broadcom/bcm2835/bcm2836.c Thu Dec 12 19:21:16 2019 (r355665) @@ -658,6 +658,8 @@ bcm_lintc_probe(device_t dev) if (!ofw_bus_is_compatible(dev, "brcm,bcm2836-l1-intc")) return (ENXIO); + if (!ofw_bus_has_prop(dev, "interrupt-controller")) + return (ENXIO); device_set_desc(dev, "BCM2836 Interrupt Controller"); return (BUS_PROBE_DEFAULT); } Modified: stable/12/sys/arm/broadcom/bcm2835/files.bcm283x ============================================================================== --- stable/12/sys/arm/broadcom/bcm2835/files.bcm283x Thu Dec 12 19:17:30 2019 (r355664) +++ stable/12/sys/arm/broadcom/bcm2835/files.bcm283x Thu Dec 12 19:21:16 2019 (r355665) @@ -14,6 +14,7 @@ arm/broadcom/bcm2835/bcm2835_rng.c optional random arm/broadcom/bcm2835/bcm2835_sdhci.c optional sdhci *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201912121921.xBCJLGSQ095280>