Path: blob/main/sys/arm/freescale/imx/imx6_sdma.c
108735 views
/*-1* Copyright (c) 2015 Ruslan Bukin <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526/*27* i.MX6 Smart Direct Memory Access Controller (sDMA)28* Chapter 41, i.MX 6Dual/6Quad Applications Processor Reference Manual,29* Rev. 1, 04/201330*/3132#include <sys/param.h>33#include <sys/systm.h>34#include <sys/bus.h>35#include <sys/kernel.h>36#include <sys/module.h>37#include <sys/malloc.h>38#include <sys/endian.h>39#include <sys/rman.h>40#include <sys/timeet.h>41#include <sys/timetc.h>42#include <sys/firmware.h>4344#include <vm/vm.h>45#include <vm/vm_extern.h>46#include <vm/vm_kern.h>47#include <vm/pmap.h>4849#include <dev/ofw/openfirm.h>50#include <dev/ofw/ofw_bus.h>51#include <dev/ofw/ofw_bus_subr.h>5253#include <machine/bus.h>54#include <machine/cpu.h>55#include <machine/intr.h>5657#include <arm/freescale/imx/imx6_sdma.h>5859#define MAX_BD (PAGE_SIZE / sizeof(struct sdma_buffer_descriptor))6061#define READ4(_sc, _reg) \62bus_space_read_4(_sc->bst, _sc->bsh, _reg)63#define WRITE4(_sc, _reg, _val) \64bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val)6566struct sdma_softc *sdma_sc;6768static struct resource_spec sdma_spec[] = {69{ SYS_RES_MEMORY, 0, RF_ACTIVE },70{ SYS_RES_IRQ, 0, RF_ACTIVE },71{ -1, 0 }72};7374/*75* This will get set to true if we can't load firmware while attaching, to76* prevent multiple attempts to re-attach the device on each bus pass.77*/78static bool firmware_unavailable;7980static void81sdma_intr(void *arg)82{83struct sdma_buffer_descriptor *bd;84struct sdma_channel *channel;85struct sdma_conf *conf;86struct sdma_softc *sc;87int pending;88int i;89int j;9091sc = arg;9293pending = READ4(sc, SDMAARM_INTR);9495/* Ack intr */96WRITE4(sc, SDMAARM_INTR, pending);9798for (i = 0; i < SDMA_N_CHANNELS; i++) {99if ((pending & (1 << i)) == 0)100continue;101channel = &sc->channel[i];102conf = channel->conf;103if (!conf)104continue;105for (j = 0; j < conf->num_bd; j++) {106bd = &channel->bd[j];107bd->mode.status |= BD_DONE;108if (bd->mode.status & BD_RROR)109printf("sDMA error\n");110}111112conf->ih(conf->ih_user, 1);113114WRITE4(sc, SDMAARM_HSTART, (1 << i));115}116}117118static int119sdma_probe(device_t dev)120{121122if (!ofw_bus_status_okay(dev) || firmware_unavailable)123return (ENXIO);124125if (!ofw_bus_is_compatible(dev, "fsl,imx6q-sdma"))126return (ENXIO);127128device_set_desc(dev, "i.MX6 Smart Direct Memory Access Controller");129return (BUS_PROBE_DEFAULT);130}131132int133sdma_start(int chn)134{135struct sdma_softc *sc;136137sc = sdma_sc;138139WRITE4(sc, SDMAARM_HSTART, (1 << chn));140141return (0);142}143144int145sdma_stop(int chn)146{147struct sdma_softc *sc;148149sc = sdma_sc;150151WRITE4(sc, SDMAARM_STOP_STAT, (1 << chn));152153return (0);154}155156int157sdma_alloc(void)158{159struct sdma_channel *channel;160struct sdma_softc *sc;161int found;162int chn;163int i;164165sc = sdma_sc;166found = 0;167168/* Channel 0 can't be used */169for (i = 1; i < SDMA_N_CHANNELS; i++) {170channel = &sc->channel[i];171if (channel->in_use == 0) {172channel->in_use = 1;173found = 1;174break;175}176}177178if (!found)179return (-1);180181chn = i;182183/* Allocate area for buffer descriptors */184channel->bd = kmem_alloc_contig(PAGE_SIZE, M_ZERO, 0, ~0,185PAGE_SIZE, 0, VM_MEMATTR_UNCACHEABLE);186187return (chn);188}189190int191sdma_free(int chn)192{193struct sdma_channel *channel;194struct sdma_softc *sc;195196sc = sdma_sc;197198channel = &sc->channel[chn];199channel->in_use = 0;200201kmem_free(channel->bd, PAGE_SIZE);202203return (0);204}205206static int207sdma_overrides(struct sdma_softc *sc, int chn,208int evt, int host, int dsp)209{210int reg;211212/* Ignore sDMA requests */213reg = READ4(sc, SDMAARM_EVTOVR);214if (evt)215reg |= (1 << chn);216else217reg &= ~(1 << chn);218WRITE4(sc, SDMAARM_EVTOVR, reg);219220/* Ignore enable bit (HE) */221reg = READ4(sc, SDMAARM_HOSTOVR);222if (host)223reg |= (1 << chn);224else225reg &= ~(1 << chn);226WRITE4(sc, SDMAARM_HOSTOVR, reg);227228/* Prevent sDMA channel from starting */229reg = READ4(sc, SDMAARM_DSPOVR);230if (!dsp)231reg |= (1 << chn);232else233reg &= ~(1 << chn);234WRITE4(sc, SDMAARM_DSPOVR, reg);235236return (0);237}238239int240sdma_configure(int chn, struct sdma_conf *conf)241{242struct sdma_buffer_descriptor *bd0;243struct sdma_buffer_descriptor *bd;244struct sdma_context_data *context;245struct sdma_channel *channel;246struct sdma_softc *sc;247#if 0248int timeout;249int ret;250#endif251int i;252253sc = sdma_sc;254255channel = &sc->channel[chn];256channel->conf = conf;257258/* Ensure operation has stopped */259sdma_stop(chn);260261/* Set priority and enable the channel */262WRITE4(sc, SDMAARM_SDMA_CHNPRI(chn), 1);263WRITE4(sc, SDMAARM_CHNENBL(conf->event), (1 << chn));264265sdma_overrides(sc, chn, 0, 0, 0);266267if (conf->num_bd > MAX_BD) {268device_printf(sc->dev, "Error: too much buffer"269" descriptors requested\n");270return (-1);271}272273for (i = 0; i < conf->num_bd; i++) {274bd = &channel->bd[i];275bd->mode.command = conf->command;276bd->mode.status = BD_DONE | BD_EXTD | BD_CONT | BD_INTR;277if (i == (conf->num_bd - 1))278bd->mode.status |= BD_WRAP;279bd->mode.count = conf->period;280bd->buffer_addr = conf->saddr + (conf->period * i);281bd->ext_buffer_addr = 0;282}283284sc->ccb[chn].base_bd_ptr = vtophys(channel->bd);285sc->ccb[chn].current_bd_ptr = vtophys(channel->bd);286287/*288* Load context.289*290* i.MX6 Reference Manual: Appendix A SDMA Scripts291* A.3.1.7.1 (mcu_2_app)292*/293294/*295* TODO: allow using other scripts296*/297context = sc->context;298memset(context, 0, sizeof(*context));299context->channel_state.pc = sc->fw_scripts->mcu_2_app_addr;300301/*302* Tx FIFO 0 address (r6)303* Event_mask (r1)304* Event2_mask (r0)305* Watermark level (r7)306*/307308if (conf->event > 32) {309context->gReg[0] = (1 << (conf->event % 32));310context->gReg[1] = 0;311} else {312context->gReg[0] = 0;313context->gReg[1] = (1 << conf->event);314}315316context->gReg[6] = conf->daddr;317context->gReg[7] = conf->word_length;318319bd0 = sc->bd0;320bd0->mode.command = C0_SETDM;321bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD;322bd0->mode.count = sizeof(*context) / 4;323bd0->buffer_addr = sc->context_phys;324bd0->ext_buffer_addr = 2048 + (sizeof(*context) / 4) * chn;325326WRITE4(sc, SDMAARM_HSTART, 1);327328#if 0329/* Debug purposes */330331timeout = 1000;332while (!(ret = READ4(sc, SDMAARM_INTR) & 1)) {333if (timeout-- <= 0)334break;335DELAY(10);336};337338if (!ret) {339device_printf(sc->dev, "Failed to load context.\n");340return (-1);341}342343WRITE4(sc, SDMAARM_INTR, ret);344345device_printf(sc->dev, "Context loaded successfully.\n");346#endif347348return (0);349}350351static int352load_firmware(struct sdma_softc *sc)353{354const struct sdma_firmware_header *header;355const struct firmware *fp;356357fp = firmware_get("sdma-imx6q");358if (fp == NULL) {359device_printf(sc->dev, "Can't get firmware.\n");360return (-1);361}362363header = fp->data;364if (header->magic != FW_HEADER_MAGIC) {365device_printf(sc->dev, "Can't use firmware.\n");366return (-1);367}368369sc->fw_header = header;370sc->fw_scripts = (const void *)((const char *)header +371header->script_addrs_start);372373return (0);374}375376static int377boot_firmware(struct sdma_softc *sc)378{379struct sdma_buffer_descriptor *bd0;380const uint32_t *ram_code;381int timeout;382int ret;383int chn;384int sz;385int i;386387ram_code = (const void *)((const char *)sc->fw_header +388sc->fw_header->ram_code_start);389390/* Make sure SDMA has not started yet */391WRITE4(sc, SDMAARM_MC0PTR, 0);392393sz = SDMA_N_CHANNELS * sizeof(struct sdma_channel_control) + \394sizeof(struct sdma_context_data);395sc->ccb = kmem_alloc_contig(sz, M_ZERO, 0, ~0, PAGE_SIZE, 0,396VM_MEMATTR_UNCACHEABLE);397sc->ccb_phys = vtophys(sc->ccb);398399sc->context = (void *)((char *)sc->ccb + \400SDMA_N_CHANNELS * sizeof(struct sdma_channel_control));401sc->context_phys = vtophys(sc->context);402403/* Disable all the channels */404for (i = 0; i < SDMA_N_EVENTS; i++)405WRITE4(sc, SDMAARM_CHNENBL(i), 0);406407/* All channels have priority 0 */408for (i = 0; i < SDMA_N_CHANNELS; i++)409WRITE4(sc, SDMAARM_SDMA_CHNPRI(i), 0);410411/* Channel 0 is used for booting firmware */412chn = 0;413414sc->bd0 = kmem_alloc_contig(PAGE_SIZE, M_ZERO, 0, ~0, PAGE_SIZE,4150, VM_MEMATTR_UNCACHEABLE);416bd0 = sc->bd0;417sc->ccb[chn].base_bd_ptr = vtophys(bd0);418sc->ccb[chn].current_bd_ptr = vtophys(bd0);419420WRITE4(sc, SDMAARM_SDMA_CHNPRI(chn), 1);421422sdma_overrides(sc, chn, 1, 0, 0);423424/* XXX: not sure what is that */425WRITE4(sc, SDMAARM_CHN0ADDR, 0x4050);426427WRITE4(sc, SDMAARM_CONFIG, 0);428WRITE4(sc, SDMAARM_MC0PTR, sc->ccb_phys);429WRITE4(sc, SDMAARM_CONFIG, CONFIG_CSM);430WRITE4(sc, SDMAARM_SDMA_CHNPRI(chn), 1);431432bd0->mode.command = C0_SETPM;433bd0->mode.status = BD_DONE | BD_INTR | BD_WRAP | BD_EXTD;434bd0->mode.count = sc->fw_header->ram_code_size / 2;435bd0->buffer_addr = vtophys(ram_code);436bd0->ext_buffer_addr = sc->fw_scripts->ram_code_start_addr;437438WRITE4(sc, SDMAARM_HSTART, 1);439440timeout = 100;441while (!(ret = READ4(sc, SDMAARM_INTR) & 1)) {442if (timeout-- <= 0)443break;444DELAY(10);445}446447if (ret == 0) {448device_printf(sc->dev, "SDMA failed to boot\n");449return (-1);450}451452WRITE4(sc, SDMAARM_INTR, ret);453454#if 0455device_printf(sc->dev, "SDMA booted successfully.\n");456#endif457458/* Debug is disabled */459WRITE4(sc, SDMAARM_ONCE_ENB, 0);460461return (0);462}463464static int465sdma_attach(device_t dev)466{467struct sdma_softc *sc;468int err;469470sc = device_get_softc(dev);471sc->dev = dev;472473if (load_firmware(sc) == -1) {474firmware_unavailable = true;475return (ENXIO);476}477478if (bus_alloc_resources(dev, sdma_spec, sc->res)) {479device_printf(dev, "could not allocate resources\n");480return (ENXIO);481}482483/* Memory interface */484sc->bst = rman_get_bustag(sc->res[0]);485sc->bsh = rman_get_bushandle(sc->res[0]);486487sdma_sc = sc;488489/* Setup interrupt handler */490err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,491NULL, sdma_intr, sc, &sc->ih);492if (err) {493device_printf(dev, "Unable to alloc interrupt resource.\n");494return (ENXIO);495}496497if (boot_firmware(sc) == -1)498return (ENXIO);499500return (0);501};502503static device_method_t sdma_methods[] = {504/* Device interface */505DEVMETHOD(device_probe, sdma_probe),506DEVMETHOD(device_attach, sdma_attach),507{ 0, 0 }508};509510static driver_t sdma_driver = {511"sdma",512sdma_methods,513sizeof(struct sdma_softc),514};515516/* We want to attach after all interrupt controllers, before anything else. */517EARLY_DRIVER_MODULE(sdma, simplebus, sdma_driver, 0, 0,518BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST);519520521