Index: head/sys/arm/freescale/imx/imx6_sdma.c =================================================================== --- head/sys/arm/freescale/imx/imx6_sdma.c (revision 310343) +++ head/sys/arm/freescale/imx/imx6_sdma.c (revision 310344) @@ -1,517 +1,518 @@ /*- * Copyright (c) 2015 Ruslan Bukin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * i.MX6 Smart Direct Memory Access Controller (sDMA) * Chapter 41, i.MX 6Dual/6Quad Applications Processor Reference Manual, * Rev. 1, 04/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_BD (PAGE_SIZE / sizeof(struct sdma_buffer_descriptor)) #define READ4(_sc, _reg) \ bus_space_read_4(_sc->bst, _sc->bsh, _reg) #define WRITE4(_sc, _reg, _val) \ bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val) struct sdma_softc *sdma_sc; static struct resource_spec sdma_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { -1, 0 } }; static void sdma_intr(void *arg) { struct sdma_buffer_descriptor *bd; struct sdma_channel *channel; struct sdma_conf *conf; struct sdma_softc *sc; int pending; int i; int j; sc = arg; pending = READ4(sc, SDMAARM_INTR); /* Ack intr */ WRITE4(sc, SDMAARM_INTR, pending); for (i = 0; i < SDMA_N_CHANNELS; i++) { if ((pending & (1 << i)) == 0) continue; channel = &sc->channel[i]; conf = channel->conf; if (!conf) continue; for (j = 0; j < conf->num_bd; j++) { bd = &channel->bd[j]; bd->mode.status |= BD_DONE; if (bd->mode.status & BD_RROR) printf("sDMA error\n"); } conf->ih(conf->ih_user, 1); WRITE4(sc, SDMAARM_HSTART, (1 << i)); } } static int sdma_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,imx6q-sdma")) return (ENXIO); device_set_desc(dev, "i.MX6 Smart Direct Memory Access Controller"); return (BUS_PROBE_DEFAULT); } int sdma_start(int chn) { struct sdma_softc *sc; sc = sdma_sc; WRITE4(sc, SDMAARM_HSTART, (1 << chn)); return (0); } int sdma_stop(int chn) { struct sdma_softc *sc; sc = sdma_sc; WRITE4(sc, SDMAARM_STOP_STAT, (1 << chn)); return (0); } int sdma_alloc(void) { struct sdma_channel *channel; struct sdma_softc *sc; int found; int chn; int i; sc = sdma_sc; found = 0; /* Channel 0 can't be used */ for (i = 1; i < SDMA_N_CHANNELS; i++) { channel = &sc->channel[i]; if (channel->in_use == 0) { channel->in_use = 1; found = 1; break; } } if (!found) return (-1); chn = i; /* Allocate area for buffer descriptors */ channel->bd = (void *)kmem_alloc_contig(kernel_arena, PAGE_SIZE, M_ZERO, 0, ~0, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE); return (chn); } int sdma_free(int chn) { struct sdma_channel *channel; struct sdma_softc *sc; sc = sdma_sc; channel = &sc->channel[chn]; channel->in_use = 0; kmem_free(kernel_arena, (vm_offset_t)channel->bd, PAGE_SIZE); return (0); } static int sdma_overrides(struct sdma_softc *sc, int chn, int evt, int host, int dsp) { int reg; /* Ignore sDMA requests */ reg = READ4(sc, SDMAARM_EVTOVR); if (evt) reg |= (1 << chn); else reg &= ~(1 << chn); WRITE4(sc, SDMAARM_EVTOVR, reg); /* Ignore enable bit (HE) */ reg = READ4(sc, SDMAARM_HOSTOVR); if (host) reg |= (1 << chn); else reg &= ~(1 << chn); WRITE4(sc, SDMAARM_HOSTOVR, reg); /* Prevent sDMA channel from starting */ reg = READ4(sc, SDMAARM_DSPOVR); if (!dsp) reg |= (1 << chn); else reg &= ~(1 << chn); WRITE4(sc, SDMAARM_DSPOVR, reg); return (0); } int sdma_configure(int chn, struct sdma_conf *conf) { struct sdma_buffer_descriptor *bd0; struct sdma_buffer_descriptor *bd; struct sdma_context_data *context; struct sdma_channel *channel; struct sdma_softc *sc; #if 0 int timeout; int ret; #endif int i; sc = sdma_sc; channel = &sc->channel[chn]; channel->conf = conf; /* Ensure operation has stopped */ sdma_stop(chn); /* Set priority and enable the channel */ WRITE4(sc, SDMAARM_SDMA_CHNPRI(chn), 1); WRITE4(sc, SDMAARM_CHNENBL(conf->event), (1 << chn)); sdma_overrides(sc, chn, 0, 0, 0); if (conf->num_bd > MAX_BD) { device_printf(sc->dev, "Error: too much buffer" " descriptors requested\n"); return (-1); } for (i = 0; i < conf->num_bd; i++) { bd = &channel->bd[i]; bd->mode.command = conf->command; bd->mode.status = BD_DONE | BD_EXTD | BD_CONT | BD_INTR; if (i == (conf->num_bd - 1)) bd->mode.status |= BD_WRAP; bd->mode.count = conf->period; bd->buffer_addr = conf->saddr + (conf->period * i); bd->ext_buffer_addr = 0; } sc->ccb[chn].base_bd_ptr = vtophys(channel->bd); sc->ccb[chn].current_bd_ptr = vtophys(channel->bd); /* * Load context. * * i.MX6 Reference Manual: Appendix A SDMA Scripts * A.3.1.7.1 (mcu_2_app) */ /* * TODO: allow using other scripts */ context = sc->context; memset(context, 0, sizeof(*context)); context->channel_state.pc = sc->fw_scripts->mcu_2_app_addr; /* * Tx FIFO 0 address (r6) * Event_mask (r1) * Event2_mask (r0) * Watermark level (r7) */ if (conf->event > 32) { context->gReg[0] = (1 << (conf->event % 32)); context->gReg[1] = 0; } else { context->gReg[0] = 0; context->gReg[1] = (1 << conf->event); } context->gReg[6] = conf->daddr; context->gReg[7] = conf->word_length; bd0 = sc->bd0; bd0->mode.command = C0_SETDM; bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; bd0->mode.count = sizeof(*context) / 4; bd0->buffer_addr = sc->context_phys; bd0->ext_buffer_addr = 2048 + (sizeof(*context) / 4) * chn; WRITE4(sc, SDMAARM_HSTART, 1); #if 0 /* Debug purposes */ timeout = 1000; while (!(ret = READ4(sc, SDMAARM_INTR) & 1)) { if (timeout-- <= 0) break; DELAY(10); }; if (!ret) { device_printf(sc->dev, "Failed to load context.\n"); return (-1); } WRITE4(sc, SDMAARM_INTR, ret); device_printf(sc->dev, "Context loaded successfully.\n"); #endif return (0); } static int load_firmware(struct sdma_softc *sc) { - struct sdma_firmware_header *header; + const struct sdma_firmware_header *header; const struct firmware *fp; fp = firmware_get("sdma_fw"); if (fp == NULL) { device_printf(sc->dev, "Can't get firmware.\n"); return (-1); } - header = (struct sdma_firmware_header *)fp->data; + header = fp->data; if (header->magic != FW_HEADER_MAGIC) { device_printf(sc->dev, "Can't use firmware.\n"); return (-1); } sc->fw_header = header; - sc->fw_scripts = (void *)((char *)header + + sc->fw_scripts = (const void *)((const char *)header + header->script_addrs_start); return (0); } static int boot_firmware(struct sdma_softc *sc) { struct sdma_buffer_descriptor *bd0; - uint32_t *ram_code; + const uint32_t *ram_code; int timeout; int ret; int chn; int sz; int i; - ram_code = (void *)((char *)sc->fw_header + + ram_code = (const void *)((const char *)sc->fw_header + sc->fw_header->ram_code_start); /* Make sure SDMA has not started yet */ WRITE4(sc, SDMAARM_MC0PTR, 0); sz = SDMA_N_CHANNELS * sizeof(struct sdma_channel_control) + \ sizeof(struct sdma_context_data); sc->ccb = (void *)kmem_alloc_contig(kernel_arena, sz, M_ZERO, 0, ~0, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE); sc->ccb_phys = vtophys(sc->ccb); sc->context = (void *)((char *)sc->ccb + \ SDMA_N_CHANNELS * sizeof(struct sdma_channel_control)); sc->context_phys = vtophys(sc->context); /* Disable all the channels */ for (i = 0; i < SDMA_N_EVENTS; i++) WRITE4(sc, SDMAARM_CHNENBL(i), 0); /* All channels have priority 0 */ for (i = 0; i < SDMA_N_CHANNELS; i++) WRITE4(sc, SDMAARM_SDMA_CHNPRI(i), 0); /* Channel 0 is used for booting firmware */ chn = 0; sc->bd0 = (void *)kmem_alloc_contig(kernel_arena, PAGE_SIZE, M_ZERO, 0, ~0, PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE); bd0 = sc->bd0; sc->ccb[chn].base_bd_ptr = vtophys(bd0); sc->ccb[chn].current_bd_ptr = vtophys(bd0); WRITE4(sc, SDMAARM_SDMA_CHNPRI(chn), 1); sdma_overrides(sc, chn, 1, 0, 0); /* XXX: not sure what is that */ WRITE4(sc, SDMAARM_CHN0ADDR, 0x4050); WRITE4(sc, SDMAARM_CONFIG, 0); WRITE4(sc, SDMAARM_MC0PTR, sc->ccb_phys); WRITE4(sc, SDMAARM_CONFIG, CONFIG_CSM); WRITE4(sc, SDMAARM_SDMA_CHNPRI(chn), 1); bd0->mode.command = C0_SETPM; bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD; bd0->mode.count = sc->fw_header->ram_code_size / 2; bd0->buffer_addr = vtophys(ram_code); bd0->ext_buffer_addr = sc->fw_scripts->ram_code_start_addr; WRITE4(sc, SDMAARM_HSTART, 1); timeout = 100; while (!(ret = READ4(sc, SDMAARM_INTR) & 1)) { if (timeout-- <= 0) break; DELAY(10); } if (ret == 0) { device_printf(sc->dev, "SDMA failed to boot\n"); return (-1); } WRITE4(sc, SDMAARM_INTR, ret); #if 0 device_printf(sc->dev, "SDMA booted successfully.\n"); #endif /* Debug is disabled */ WRITE4(sc, SDMAARM_ONCE_ENB, 0); return (0); } static int sdma_attach(device_t dev) { struct sdma_softc *sc; int err; sc = device_get_softc(dev); sc->dev = dev; if (bus_alloc_resources(dev, sdma_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); sdma_sc = sc; /* Setup interrupt handler */ err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, NULL, sdma_intr, sc, &sc->ih); if (err) { device_printf(dev, "Unable to alloc interrupt resource.\n"); return (ENXIO); } if (load_firmware(sc) == -1) return (ENXIO); if (boot_firmware(sc) == -1) return (ENXIO); return (0); }; static device_method_t sdma_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sdma_probe), DEVMETHOD(device_attach, sdma_attach), { 0, 0 } }; static driver_t sdma_driver = { "sdma", sdma_methods, sizeof(struct sdma_softc), }; static devclass_t sdma_devclass; -DRIVER_MODULE(sdma, simplebus, sdma_driver, sdma_devclass, 0, 0); +EARLY_DRIVER_MODULE(sdma, simplebus, sdma_driver, sdma_devclass, 0, 0, + BUS_PASS_RESOURCE); Index: head/sys/arm/freescale/imx/imx6_sdma.h =================================================================== --- head/sys/arm/freescale/imx/imx6_sdma.h (revision 310343) +++ head/sys/arm/freescale/imx/imx6_sdma.h (revision 310344) @@ -1,245 +1,245 @@ /*- * Copyright (c) 2015 Ruslan Bukin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #define SDMAARM_MC0PTR 0x00 /* ARM platform Channel 0 Pointer */ #define SDMAARM_INTR 0x04 /* Channel Interrupts */ #define SDMAARM_STOP_STAT 0x08 /* Channel Stop/Channel Status */ #define SDMAARM_HSTART 0x0C /* Channel Start */ #define SDMAARM_EVTOVR 0x10 /* Channel Event Override */ #define SDMAARM_DSPOVR 0x14 /* Channel BP Override */ #define SDMAARM_HOSTOVR 0x18 /* Channel ARM platform Override */ #define SDMAARM_EVTPEND 0x1C /* Channel Event Pending */ #define SDMAARM_RESET 0x24 /* Reset Register */ #define SDMAARM_EVTERR 0x28 /* DMA Request Error Register */ #define SDMAARM_INTRMASK 0x2C /* Channel ARM platform Interrupt Mask */ #define SDMAARM_PSW 0x30 /* Schedule Status */ #define SDMAARM_EVTERRDBG 0x34 /* DMA Request Error Register */ #define SDMAARM_CONFIG 0x38 /* Configuration Register */ #define CONFIG_CSM 0x3 #define SDMAARM_SDMA_LOCK 0x3C /* SDMA LOCK */ #define SDMAARM_ONCE_ENB 0x40 /* OnCE Enable */ #define SDMAARM_ONCE_DATA 0x44 /* OnCE Data Register */ #define SDMAARM_ONCE_INSTR 0x48 /* OnCE Instruction Register */ #define SDMAARM_ONCE_STAT 0x4C /* OnCE Status Register */ #define SDMAARM_ONCE_CMD 0x50 /* OnCE Command Register */ #define SDMAARM_ILLINSTADDR 0x58 /* Illegal Instruction Trap Address */ #define SDMAARM_CHN0ADDR 0x5C /* Channel 0 Boot Address */ #define SDMAARM_EVT_MIRROR 0x60 /* DMA Requests */ #define SDMAARM_EVT_MIRROR2 0x64 /* DMA Requests 2 */ #define SDMAARM_XTRIG_CONF1 0x70 /* Cross-Trigger Events Configuration Register 1 */ #define SDMAARM_XTRIG_CONF2 0x74 /* Cross-Trigger Events Configuration Register 2 */ #define SDMAARM_SDMA_CHNPRI(n) (0x100 + 0x4 * n) /* Channel Priority Registers */ #define SDMAARM_CHNENBL(n) (0x200 + 0x4 * n) /* Channel Enable RAM */ /* SDMA Event Mappings */ #define SSI1_RX_1 35 #define SSI1_TX_1 36 #define SSI1_RX_0 37 #define SSI1_TX_0 38 #define SSI2_RX_1 39 #define SSI2_TX_1 40 #define SSI2_RX_0 41 #define SSI2_TX_0 42 #define SSI3_RX_1 43 #define SSI3_TX_1 44 #define SSI3_RX_0 45 #define SSI3_TX_0 46 #define C0_ADDR 0x01 #define C0_LOAD 0x02 #define C0_DUMP 0x03 #define C0_SETCTX 0x07 #define C0_GETCTX 0x03 #define C0_SETDM 0x01 #define C0_SETPM 0x04 #define C0_GETDM 0x02 #define C0_GETPM 0x08 #define BD_DONE 0x01 #define BD_WRAP 0x02 #define BD_CONT 0x04 #define BD_INTR 0x08 #define BD_RROR 0x10 #define BD_LAST 0x20 #define BD_EXTD 0x80 /* sDMA data transfer length */ #define CMD_4BYTES 0 #define CMD_3BYTES 3 #define CMD_2BYTES 2 #define CMD_1BYTES 1 struct sdma_firmware_header { uint32_t magic; uint32_t version_major; uint32_t version_minor; uint32_t script_addrs_start; uint32_t num_script_addrs; uint32_t ram_code_start; uint32_t ram_code_size; }; struct sdma_mode_count { uint16_t count; uint8_t status; uint8_t command; }; struct sdma_buffer_descriptor { struct sdma_mode_count mode; uint32_t buffer_addr; uint32_t ext_buffer_addr; } __packed; struct sdma_channel_control { uint32_t current_bd_ptr; uint32_t base_bd_ptr; uint32_t unused[2]; } __packed; struct sdma_state_registers { uint32_t pc :14; uint32_t unused1: 1; uint32_t t : 1; uint32_t rpc :14; uint32_t unused0: 1; uint32_t sf : 1; uint32_t spc :14; uint32_t unused2: 1; uint32_t df : 1; uint32_t epc :14; uint32_t lm : 2; } __packed; struct sdma_context_data { struct sdma_state_registers channel_state; uint32_t gReg[8]; uint32_t mda; uint32_t msa; uint32_t ms; uint32_t md; uint32_t pda; uint32_t psa; uint32_t ps; uint32_t pd; uint32_t ca; uint32_t cs; uint32_t dda; uint32_t dsa; uint32_t ds; uint32_t dd; uint32_t unused[8]; } __packed; /* SDMA firmware script pointers */ struct sdma_script_start_addrs { int32_t ap_2_ap_addr; int32_t ap_2_bp_addr; int32_t ap_2_ap_fixed_addr; int32_t bp_2_ap_addr; int32_t loopback_on_dsp_side_addr; int32_t mcu_interrupt_only_addr; int32_t firi_2_per_addr; int32_t firi_2_mcu_addr; int32_t per_2_firi_addr; int32_t mcu_2_firi_addr; int32_t uart_2_per_addr; int32_t uart_2_mcu_addr; int32_t per_2_app_addr; int32_t mcu_2_app_addr; int32_t per_2_per_addr; int32_t uartsh_2_per_addr; int32_t uartsh_2_mcu_addr; int32_t per_2_shp_addr; int32_t mcu_2_shp_addr; int32_t ata_2_mcu_addr; int32_t mcu_2_ata_addr; int32_t app_2_per_addr; int32_t app_2_mcu_addr; int32_t shp_2_per_addr; int32_t shp_2_mcu_addr; int32_t mshc_2_mcu_addr; int32_t mcu_2_mshc_addr; int32_t spdif_2_mcu_addr; int32_t mcu_2_spdif_addr; int32_t asrc_2_mcu_addr; int32_t ext_mem_2_ipu_addr; int32_t descrambler_addr; int32_t dptc_dvfs_addr; int32_t utra_addr; int32_t ram_code_start_addr; int32_t mcu_2_ssish_addr; int32_t ssish_2_mcu_addr; int32_t hdmi_dma_addr; }; #define SDMA_N_CHANNELS 32 #define SDMA_N_EVENTS 48 #define FW_HEADER_MAGIC 0x414d4453 struct sdma_channel { struct sdma_conf *conf; struct sdma_buffer_descriptor *bd; uint8_t in_use; }; struct sdma_softc { struct resource *res[2]; bus_space_tag_t bst; bus_space_handle_t bsh; device_t dev; void *ih; struct sdma_channel_control *ccb; struct sdma_buffer_descriptor *bd0; struct sdma_context_data *context; struct sdma_channel channel[SDMA_N_CHANNELS]; uint32_t num_bd; uint32_t ccb_phys; uint32_t context_phys; - struct sdma_firmware_header *fw_header; - struct sdma_script_start_addrs *fw_scripts; + const struct sdma_firmware_header *fw_header; + const struct sdma_script_start_addrs *fw_scripts; }; struct sdma_conf { bus_addr_t saddr; bus_addr_t daddr; uint32_t word_length; uint32_t nbits; uint32_t command; uint32_t num_bd; uint32_t event; uint32_t period; uint32_t (*ih)(void *, int); void *ih_user; }; int sdma_configure(int, struct sdma_conf *); int sdma_start(int); int sdma_stop(int); int sdma_alloc(void); int sdma_free(int);