Subject: Fwd: question about mmap
To: None <tech-kern@netbsd.org>
From: Chan-youn Park <phygeeks@gmail.com>
List: tech-kern
Date: 03/10/2005 09:34:21
---------- Forwarded message ----------
From: Chan-youn Park <phygeeks@gmail.com>
Date: Thu, 10 Mar 2005 09:20:29 +0900
Subject: Re: question about mmap
To: Jachym Holecek <freza@liberouter.org>


On Wed, 9 Mar 2005 10:01:57 +0100, Jachym Holecek <freza@liberouter.org> wrote:
> Hi,
>
> > [... Can't bus_space_mmap(), mmap routine looks correct ...]
> >
> > here, pmemt and pmema is the tag and physical address of the DSP
> > memory space which are get using pci_mapreg_info.
>
> Just to make sure -- do you 1) also call pci_mapreg_map() or 2) call
> bus_space_map() before allowing accessing that space by method?

I call bus_space_map() using the information get from pci_mapreg_info().

> Btw, memory protection value of "1" you're seeing is relevant and means
> "allow reads" (PROT_READ that is, see /usr/include/sys/mman.h), which is
> correct.

Yes of course. What I meant was I was reading the DSP memory space,
and so PROT_READ will do.

> > And below is a part of a test application program to see the DSP memory space.
> > ----
> > tdsph = open("/dev/tdsp", O_RDWR, 0);
> > sram.addr = mmap(0, sram.len, PROT_READ, MAP_SHARED, tdsph,
> >             (off_t)sram.offset);
>
> Does mmap() succeed? Is sram.offset really correct (relative to start of
> SRAM window)?

Unfortunately yes. And offset is also correct because if I use the
offset to display the DSP memory space in kernel (I give the offset to
kernel by ioctl, for debugging), I get the correct results. (I
initialized the DSP memory for each address to contain the value of
its address).

> >  while(i < sram.len){
> >                 addr = (u_int8_t*)sram.addr + i;
> >  [...]
>
> Does the DSP SRAM (and the bus hierarchy it's connected to) allow you 8bit
> accesses?

I'm not sure, but I've used 8 bit accesses in kernel, so I think yes.

> > What should I check to see what is wrong? And in addition, is there a
> > good simple code that I can study the usage of device mmap function?
>
> Hmm, I couldn't see anything obviously wrong. After checking the
> questions above, we can look at more code...
>
>        Regards,
>                -- Jachym Holecek
>
Well then It's time to attach the whole code itself ;)

First some attach routines for DSP drivers.

----
/*      $XEplus: tdsp_pci.c,v 1.1 2004/12/22 06:56:45 hebites Exp $     */

/*
* DSP (TMS320C6415) pci attach routine
*/

#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>

#include <machine/bus.h>

#include <dev/pci/pcidevs.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>

#include <arch/sandpoint/xepro/tdsp_reg.h>
#include <arch/sandpoint/xepro/tdsp.h>

#define HERE    printf("%s:%d\n", __FUNCTION__, __LINE__)

/*
* TMS320C6x DSP product info
*/

struct tdsp_pci_product {
       u_int32_t       tpp_vendorid;   /* PCI Vendor ID */
       u_int32_t       tpp_prodid;     /* PCI Product ID */
       const char      *tpp_name;      /* device name */

       bus_addr_t      tpp_pmembase;   /* base 0 offset, prefetchable */
       bus_addr_t      tpp_npmembase;  /* base 1 offset, non-prefetchable */
       bus_addr_t      tpp_iobase;     /* base 2 offset, io space */
};

const struct tdsp_pci_product tdsp_pci_products[] = {
       /* TMS320c64x */
       {
               PCI_VENDOR_TI,
               PCI_PRODUCT_TI_TMS320C6415,
               "Texas Instruments TMS320C6415 DSP",
               0x10,           /* Base 0 offset, prefetchable memory */
               0x14,           /* Base 1 offset, non-prefetchable memory */
               0x18,           /* Base 2 offset, I/O space */
       },
       /* other products might come here */
       /* the end of tdsp_pci_products */
       { 0, 0, NULL, 0, 0, 0}
};

static const struct tdsp_pci_product*
tdsp_pci_lookup(const struct pci_attach_args*);

int     tdsp_pci_match __P((struct device *, struct cfdata *, void *));
void    tdsp_pci_attach __P((struct device *, struct device *, void *));

struct cfattach tdsp_pci_ca = {
       sizeof(struct tdsp_softc),
       tdsp_pci_match,
       tdsp_pci_attach
};

int
tdsp_pci_match(struct device *parent, struct cfdata *match, void *aux)
{
       struct pci_attach_args *pa = (struct pci_attach_args *) aux;

       if (tdsp_pci_lookup(pa) != NULL)
               return (1);
       return (0);
}

void
tdsp_pci_attach(struct device *parent, struct device *self, void *aux)
{
       struct tdsp_softc *sc = (struct tdsp_softc *)self;
       struct pci_attach_args *pa = aux;

       pci_intr_handle_t ih;

       /* Bus space tag & handle for PCI I/O memory space BAR */
       bus_space_tag_t         iot;
       bus_space_handle_t      ioh;
       /* Bus space tag & handle for PCI prefetchable memory space BAR */
       bus_space_tag_t         pmemt;
       bus_space_handle_t      pmemh;
       /* Bus space tag & handle for PCI nonprefetchable memory space BAR */
       bus_space_tag_t         npmemt;
       bus_space_handle_t      npmemh;

       int io_valid, pmem_valid, npmem_valid;
       const struct tdsp_pci_product *tpp;
       const char *intrstr = NULL;
       bus_addr_t addr;
       bus_size_t size;
       int flags;
       int pci_mapreg_info_retval;

       /* Check if the found one is the right DSP */

       tpp = tdsp_pci_lookup(pa);
       if (tpp == NULL) {
               ERROR("%s: couldn't find chip id\n", sc->sc_dev.dv_xname);
               return;
       }

       /* Map BAR2(I/O) register */
       io_valid =
           (pci_mapreg_map(
               pa,
               tpp->tpp_iobase,
               PCI_MAPREG_TYPE_IO,
               0,
               &iot,
               &ioh,
               NULL,
               NULL) == 0);

       /* Map BAR0(prefetchable) register */
       pmem_valid = 0;
       pmemt = pa->pa_memt;
       pci_mapreg_info_retval =
           pci_mapreg_info(
               pa->pa_pc,
               pa->pa_tag,
               tpp->tpp_pmembase,
               PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT,
               &addr,
               &size,
               &flags);
       if ((pa->pa_flags & PCI_FLAGS_MEM_ENABLED) &&
           (pci_mapreg_info_retval == 0)){
               flags &= BUS_SPACE_MAP_PREFETCHABLE;
               if (bus_space_map(pmemt, addr, size, flags, &pmemh) == 0) {
                       sc->sc_pmema = addr;
                       pmem_valid = 1;
               }
       }

       /* Map BAR1(non-prefetchable) register */
       npmem_valid = 0;
       npmemt = pa->pa_memt;
       pci_mapreg_info_retval =
           pci_mapreg_info(
               pa->pa_pc,
               pa->pa_tag,
               tpp->tpp_npmembase,
               PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT,
               &addr,
               &size,
               &flags);
       if ((pa->pa_flags & PCI_FLAGS_MEM_ENABLED) &&
           (pci_mapreg_info_retval == 0)){
               flags &= ~BUS_SPACE_MAP_PREFETCHABLE;
               if (bus_space_map(npmemt, addr, size, flags, &npmemh) == 0) {
                       sc->sc_npmema = addr;
                       npmem_valid = 1;
               }
       }

       /*
        * If all the above mappings are successful,
        * save tags & handles of the mapped bus space
        */
       if (io_valid && pmem_valid && npmem_valid) {
               sc->sc_pmemt = pmemt;
               sc->sc_pmemh = pmemh;
               sc->sc_npmemt = npmemt;
               sc->sc_npmemh = npmemh;
               sc->sc_iot = iot;
               sc->sc_ioh = ioh;
       } else {
               ERROR(": unable to map device registers\n");
               ERROR("\tio_valid = %d, pmem_valid = %d, npmem_valid = %d\n",
                   io_valid, pmem_valid, npmem_valid);
               return;
       }

       printf(": %s, rev %d\n", tpp->tpp_name, PCI_REVISION(pa->pa_class));

       /* DMA setting */
       /* XXX Is this necessary? */
       sc->sc_dmat = pa->pa_dmat;

       /* Make sure bus-mastering is enabled. */
       /* XXX is this necessary? */
       pci_conf_write(
           pa->pa_pc,
           pa->pa_tag,
           PCI_COMMAND_STATUS_REG,
           pci_conf_read(
               pa->pa_pc,
               pa->pa_tag,
               PCI_COMMAND_STATUS_REG) | PCI_COMMAND_MASTER_ENABLE);

       /* Map and establish our interrupt */
       if (pci_intr_map(pa, &ih)) {
               ERROR("%s: couldn't map interrupt\n", sc->sc_dev.dv_xname);
               return;
       }
       intrstr = pci_intr_string(pa->pa_pc, ih);
       /* XXX Is IPL_NET OK for the priority level of this PCI bus? */
       sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_NET, tdspintr, sc);
       if (sc->sc_ih == NULL) {
               ERROR("%s: couldn't establish interrupt",
                   sc->sc_dev.dv_xname);
               if (intrstr != NULL)
                       printf(" at %s", intrstr);
               printf("\n");
               return;
       }
       printf("%s: interrupting at %s\n", sc->sc_dev.dv_xname, intrstr);

       /* Finish off the attach. */
       tdsp_attach(sc);
}

static const struct tdsp_pci_product *
tdsp_pci_lookup(const struct pci_attach_args *pa)
{
       const struct tdsp_pci_product *tpp;

       if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_TI)
               return (NULL);

       for (tpp = tdsp_pci_products; tpp->tpp_name != NULL; tpp++)
               if (PCI_PRODUCT(pa->pa_id) == tpp->tpp_prodid)
                       return (tpp);
       return (NULL);
}
----
and DSP device driver itself
----
/*      $XEplus: tdsp.c,v 1.1 2004/12/22 06:56:45 hebites Exp $ */

/*
* TDSP320C6415 DSP Device
*/

#include <sys/cdefs.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/fcntl.h>

#include <machine/bus.h>
#include <arch/sandpoint/xepro/tdsp_reg.h>
#include <arch/sandpoint/xepro/tdsp.h>

#include "talker_code.h"

/* Structure for convenient EMIF initialization */

typedef struct emif_config {
       const char *name;       /* Name of the register */
       u_int32_t addr;         /* Address of the register */
       u_int32_t val;          /* Value of the register to write */
}emif_config_t;

const emif_config_t emifa[] = {
       {"EMIFA_GBLCTL",        TDSP_EMIFB_GBLCTL,      0x00012004},
       {"EMIFA_SDCTL",         TDSP_EMIFB_SDCTL,       0x63117000},
       {"EMIFA_SDTIM",         TDSP_EMIFB_SDTIM,       0x02000600},
       {"EMIFA_CE0CTL",        TDSP_EMIFB_CE0CTL,      0xffffff1f},
       {"EMIFA_CE1CTL",        TDSP_EMIFB_CE1CTL,      0xffffff1f},
       {"EMIFA_CE2CTL",        TDSP_EMIFB_CE2CTL,      0x00000090},
       {"EMIFA_CE3CTL",        TDSP_EMIFB_CE3CTL,      0x00000090},
       {"EMIFA_CE0SEC",        TDSP_EMIFB_CE0SEC,      0x00000047},
       {"EMIFA_CE1SEC",        TDSP_EMIFB_CE1SEC,      0x00000047},
       {"EMIFA_CE2SEC",        TDSP_EMIFB_CE2SEC,      0x00000047},
       {"EMIFA_CE3SEC",        TDSP_EMIFB_CE3SEC,      0x00000047},
       {NULL, -1, 0}
};

const emif_config_t emifb[] = {
       {"EMIFB_GBLCTL",        TDSP_EMIFB_GBLCTL,      0x0001200c},
       {"EMIFB_SDCTL",         TDSP_EMIFB_SDCTL,       0x63117000},
       {"EMIFB_SDTIM",         TDSP_EMIFB_SDTIM,       0x02000600},
       {"EMIFB_CE0CTL",        TDSP_EMIFB_CE0CTL,      0xffffff1f},
       {"EMIFB_CE1CTL",        TDSP_EMIFB_CE1CTL,      0xffffff1f},
       {"EMIFB_CE2CTL",        TDSP_EMIFB_CE2CTL,      0x00000090},
       {"EMIFB_CE3CTL",        TDSP_EMIFB_CE3CTL,      0x00000090},
       {"EMIFB_CE0SEC",        TDSP_EMIFB_CE0SEC,      0x00000047},
       {"EMIFB_CE1SEC",        TDSP_EMIFB_CE1SEC,      0x00000047},
       {"EMIFB_CE2SEC",        TDSP_EMIFB_CE2SEC,      0x00000047},
       {"EMIFB_CE3SEC",        TDSP_EMIFB_CE3SEC,      0x00000047},
       {NULL, -1, 0}
};

extern struct cfdriver  tdsp_cd;
/* global variable for saving current window page */
static tdsp_reg32_t     dspp;
static int              tdsp_is_open;

static u_int32_t        tdsp_read_memory
   __P((struct tdsp_softc*, tdsp_addr_t));

static void             tdsp_write_memory
   __P((struct tdsp_softc*, tdsp_addr_t, u_int32_t));

int                     tdsp_read_memory_block
   __P((struct tdsp_softc*, const tdsp_addr_t, u_int8_t*, u_int32_t));

int                     tdsp_write_memory_block
   __P((struct tdsp_softc*, const tdsp_addr_t, u_int8_t*, u_int32_t));

static void             tdsp_memory_test
   __P((struct tdsp_softc*, tdsp_addr_t, u_int32_t));

static tdsp_addr_t      tdsp_set_page
   __P((struct tdsp_softc*, tdsp_addr_t));

static int      tdsp_init_emif  __P((struct tdsp_softc*, const emif_config_t*));
//static void   tdsp_boot       __P((struct tdsp_softc*));
void    tdsp_boot       __P((struct tdsp_softc*));

void
tdsp_attach(struct tdsp_softc *sc)
{
       bus_space_tag_t memt, iot;
       bus_space_handle_t memh, ioh;
       //bus_addr_t addr;
       //u_int32_t src, mask, hsr, rstsrc;
       int i;

       memt = sc->sc_npmemt;
       memh = sc->sc_npmemh;
       iot = sc->sc_iot;
       ioh = sc->sc_ioh;

       /* Configuration of the emif control registers */

       /* EMIFA configuration */
       printf("%s: configuring EMIFA\n", sc->sc_dev.dv_xname);
       i = tdsp_init_emif(sc, emifa);
       if (emifa[i].addr != -1) {
               ERROR("%s: EMIFA is not configured.\n", sc->sc_dev.dv_xname);
       }

       /* EMIFB configuration */
       printf("%s: configure EMIFB\n", sc->sc_dev.dv_xname);
       i = tdsp_init_emif(sc, emifb);
       if (emifb[i].addr != -1) {
               ERROR("%s: EMIFB is not configured.\n", sc->sc_dev.dv_xname);
       }
#if DIAGNOSTIC
       {
               /* TODO implement memory test code */
               /* Test internal memory */
               INFO("%s: Testing internal RAM\n", sc->sc_dev.dv_xname);
               tdsp_memory_test(sc, TDSP_SRAM_BASE, TDSP_SRAM_SIZE);
               /* Test external SDRAM */
               INFO("%s: Testing EMIFB SDRAM\n", sc->sc_dev.dv_xname);
               tdsp_memory_test(sc, TDSP_EMIFB_CE2, TDSP_EMIFB_SDRAM_SIZE);
       }
#endif /* DIAGNOSTIC */
       /* Clearing PCI interrupt-related registers */

       /* Read PCI interrupt enable reigster(PCIIEN) */
/*
       mask = bus_space_read_4(memt, memh, TDSP_PERI_PCIIEN);
       DEBUGF("%s: PCIIEN = 0x%08x\n", sc->sc_dev.dv_xname, mask);
*/

       /* Read other PCI interrupt related registers */
/*
       src = bus_space_read_4(memt, memh, TDSP_PERI_PCIIS);
       hsr = bus_space_read_4(iot, ioh, TDSP_IO_HSR);
       rstsrc = bus_space_read_4(memt, memh, TDSP_PERI_RSTSRC);
       DEBUGF("%s: before clearing interrupts - \n"
           "\tPCIIS = 0x%08x, HSR = 0x%08x, RSTSRC = 0x%08x\n",
           sc->sc_dev.dv_xname, src, hsr, rstsrc);
*/
       /* Clear the registers */
       /* Clear all interrupt sources */
/*
       bus_space_write_4(memt, memh, TDSP_PERI_PCIIS, 0x00000bff);
*/
       /* Deassert and mask out /PINTA */
       bus_space_write_4(iot, ioh, TDSP_IO_HSR, 0x00000001);
       /* Deassert /PINTA */
/*
       bus_space_write_4(memt, memh, TDSP_PERI_RSTSRC, 0x00000010);
*/
       /* Read the registers again */
/*
       src = bus_space_read_4(memt, memh, TDSP_PERI_PCIIS);
       hsr = bus_space_read_4(iot, ioh, TDSP_IO_HSR);
       rstsrc = bus_space_read_4(memt, memh, TDSP_PERI_RSTSRC);
       DEBUGF("%s: after clearing interrupts - \n"
           "\tPCIIS = 0x%08x, HSR = 0x%08x, RSTSRC = 0x%08x\n",
           sc->sc_dev.dv_xname, src, hsr, rstsrc);
*/
/*      DEBUGF("phygeeks: DSP RAM contents @0x10000(before boot) = 0x%08x\n",
           tdsp_read_memory(sc, 0x10000));
       tdsp_boot(sc);
       DEBUGF("phygeeks: Wait 1s...\n");
       delay(1000000);
       DEBUGF("phygeeks: DSP RAM contents @0x10000(after boot) = 0x%08x\n",
           tdsp_read_memory(sc, 0x10000));
*/
}

int
tdspopen(dev_t dev, int ioflag, int mode, struct proc *p)
{
       struct tdsp_softc *sc;
       char *name;

       sc = device_lookup(&tdsp_cd, TDSPUNIT(dev));
       if(sc == NULL){
               ERROR("NULL sc is returned: "
                   "cannot proceed tdspopen() anymore.\n");
               return (ENXIO);
       }
       name = sc->sc_dev.dv_xname;
       if(tdsp_is_open){
               ERROR("%s: already opened.\n", name);
               return (EBUSY);
       }
       tdsp_is_open = TRUE;
       DEBUGF("%s: opened.\n", name);
       return 0;
}

int
tdspclose(dev_t dev, int ioflag, int mode, struct proc *p)
{
       struct tdsp_softc *sc;
       char *name;

       sc = device_lookup(&tdsp_cd, TDSPUNIT(dev));
       if(sc == NULL){
               ERROR("NULL sc is returned: "
                   "cannot proceed tdspclose() anymore.\n");
               return (ENXIO);
       }
       name = sc->sc_dev.dv_xname;
       if(!tdsp_is_open){
               ERROR("%s: already closed.\n", sc->sc_dev.dv_xname);
               return (EBADF);
       }
       tdsp_is_open = FALSE;
       DEBUGF("%s: closed.\n", name);
       return 0;
}

int
tdspioctl(dev_t dev, u_long cmd, caddr_t addr, int ioflag, struct proc *p)
{
       struct tdsp_softc *sc;
       char *name;

       sc = device_lookup(&tdsp_cd, TDSPUNIT(dev));
       if (sc == NULL){
               ERROR("NULL sc is returned: "
                   "cannot proceed tdspioctl() anymore.\n");
               return (ENXIO);
       }
       name = sc->sc_dev.dv_xname;

       switch (cmd) {
       case    TDSP_RESET:
               /* DSP warm reset */
               bus_space_write_4(sc->sc_iot, sc->sc_ioh, TDSP_IO_HDCR,
                   0x00000001);
               DEBUGF("%s: Reset\n", name);
               return 0;
       case    TDSP_BOOT:
               /*
                * Generates a host interrupt to the DSP.
                * This interrupt will take DSP CPU out of reset.
                */
               bus_space_write_4(sc->sc_iot, sc->sc_ioh, TDSP_IO_HDCR,
                   0x00000002);
               DEBUGF("%s: Jump to address 0\n", name);
               return 0;
       case    TDSP_SET_PAGE:
               /*
                * Set DSPP to the page of the requested address
                * and returns the offset of the address.
                */
               *(u_int32_t*)addr = tdsp_set_page(sc, *(u_int32_t*)addr);
               DEBUGF("%s: Set DSPP to 0x%03x\n",
                   name, (*(u_int32_t*)addr) >> TDSP_PAGE_WIDTH);
               return 0;
       case    TDSP_GET_PAGE:
               *(u_int32_t*)addr = dspp;
               DEBUGF("%s: DSPP is 0x%03x\n", name, dspp);
               return 0;
       case    TDSP_DISPLAY_MEMORY:
               {
                       int i = 0;
                       u_int8_t *start_addr = *(u_int8_t**)addr;
                       while(i < 0x100){
                               if(i % 16 == 0)
                                       DEBUGF("\n0x%08x: ",
                                           (int)(start_addr + i));
                               DEBUGF("%02x ", *(start_addr + i));
                               i++;
                       }
                       DEBUGF("\n");
               }
               return 0;
       default:
               return (ENOTTY);
       }
       return (ENOTTY);
}

int     tdsppoll(dev, event, p)
       dev_t dev;
       int event;
       struct proc *p;
{
       return 0;
}

/*
* XXX tdspmmap() maps an area in the PCI window, not DSP memory space itself.
* Think about a method that will enforce a user not to forget the thing.
*/
paddr_t
tdspmmap(dev_t dev, off_t off, int prot)
{
       struct tdsp_softc *sc;
       char *name;
       paddr_t paddr;

       sc = device_lookup(&tdsp_cd, TDSPUNIT(dev));
       if (sc == NULL){
               ERROR("NULL sc is returned: "
                   "cannot proceed tdspmmap() anymore.\n");
               return (ENXIO);
       }
       name = sc->sc_dev.dv_xname;

       if (off >= TDSP_PMEM_MASK || off < 0){
               ERROR("%s: Offset is out of range.\n", name);
               return -1;
       }

       DEBUGF("%s: sc_pmema = 0x%08x, off = 0x%08x\n", name, sc->sc_pmema,
           (int)off);
       paddr = bus_space_mmap(sc->sc_pmemt, sc->sc_pmema, off, prot,
           BUS_SPACE_MAP_LINEAR);
       DEBUGF("%s: return value of bus_space_mmap = 0x%08x\n", name,
           (u_int32_t)paddr);
       return paddr;
}

int
tdspintr(void* arg)
{
       struct tdsp_softc *sc = arg;
       bus_space_tag_t memt, iot;
       bus_space_handle_t memh, ioh;
       u_int32_t src, mask, hsr, rstsrc;

       memt = sc->sc_npmemt;
       memh = sc->sc_npmemh;
       iot = sc->sc_iot;
       ioh = sc->sc_ioh;

       src = bus_space_read_4(memt, memh, TDSP_PERI_PCIIS);
       mask = bus_space_read_4(memt, memh, TDSP_PERI_PCIIEN);
       hsr = bus_space_read_4(iot, ioh, TDSP_IO_HSR);
       rstsrc = bus_space_read_4(memt, memh, TDSP_PERI_RSTSRC);
       DEBUGF("%s: interrupt - "
           "PCIIS = 0x%08x, PCIIEN = 0x%08x, HSR = 0x%08x, rstsrc = 0x%08x\n",
           sc->sc_dev.dv_xname, src, mask, hsr, rstsrc);

       /* clear interrupt */
       bus_space_write_4(memt, memh, TDSP_PERI_PCIIS, 0xffffffff);
       bus_space_write_4(iot, ioh, TDSP_IO_HSR, 0xffffffff);
       bus_space_write_4(memt, memh, TDSP_PERI_RSTSRC, 0x10);

       return 0;
}

int tdsp_init_emif(struct tdsp_softc *sc, const emif_config_t* emif_configs)
{
       int i;
       bus_space_tag_t memt = sc->sc_npmemt;
       bus_space_handle_t memh = sc->sc_npmemh;
       bus_addr_t addr;

       for (i = 0; emif_configs[i].addr != -1; i++) {
               /* Set EMIF registers */
               addr = (emif_configs[i].addr);
               bus_space_write_4(memt, memh, addr, emif_configs[i].val);
#if DIAGNOSTIC
               {
                       u_int32_t val;
                       /* Wait at least 1 ms to pass the transient state */
                       delay(1);
                       /* Read the values of the registers back */
                       val = bus_space_read_4(memt, memh, addr);
                       DEBUGF("%s: %s\t- 0x%08x written,  0x%08x read\n",
                           sc->sc_dev.dv_xname, emif_configs[i].name,
                           emif_configs[i].val, val);
               }
#endif  /* DIAGNOSTIC */
       }
       return i;
}

/*
* Load boot code to DSP memory and boot DSP CPU
* XXX This should be removed in future works
*/

void tdsp_boot(struct tdsp_softc *sc)
{
       int i = 0;
       int size, addr;
       int sizeof_talker_code = sizeof(talker_code);

       INFO("%s: Load boot code\n", sc->sc_dev.dv_xname);
       /* Loop until all the code are loaded */
       while(i < sizeof_talker_code)
       {
               /*
                * Each section starts with the size of it
                * and the address to load it
                */
               u_int32_t *section = (u_int32_t*)(talker_code + i);
               /* Size of the section is stored in little endian */
               size = letobe_32(section[0]);
               /* Address of the section is stored in little endian */
               addr = letobe_32(section[1]);
               /* Code of a section starts after 8 bytes */
               i += 8;
               tdsp_write_memory_block(sc, addr, talker_code + i, size);
               INFO("%s: Boot code section of size 0x%x loaded at 0x%08x\n",
                   sc->sc_dev.dv_xname, size, addr);
               /* Now jump to another section */
               i += size;
       }
       /* Take DSP core out of reset */
       bus_space_write_4(sc->sc_iot, sc->sc_ioh, TDSP_IO_HDCR, 0x00000002);
}

static tdsp_addr_t tdsp_set_page(struct tdsp_softc* sc, tdsp_addr_t addr)
{
       /* calculate the page of the requested address */
       tdsp_reg32_t page = addr >> TDSP_PAGE_WIDTH;
       /* set the value of DSPP as the calcuated one if different */
       if (dspp != page) {
               dspp = page;
               bus_space_write_4(sc->sc_iot, sc->sc_ioh,
                   TDSP_IO_DSPP, page);
       }
       /* return the offset */
       return (addr & TDSP_PMEM_MASK);
}

/*
* Read 4 bytes from the designated address
* and return byte-ordering swapped value
*/

u_int32_t tdsp_read_memory(struct tdsp_softc* sc, tdsp_addr_t addr)
{
       tdsp_reg32_t save;
       tdsp_addr_t offset;
       le32_t ret;
       /* save DSPP to a temporary variable to restore later */
       save = dspp;
       /* set DSPP to the requested page and get the offset */
       offset = tdsp_set_page(sc, addr);
       /* read the value of the requested address */
       ret = bus_space_read_4(sc->sc_pmemt, sc->sc_pmemh, offset);
       /* restore DSPP */
       tdsp_set_page(sc, save << TDSP_PAGE_WIDTH);
       /* change the byte ordering and return */
       return ret;
}

/*
* Write 4 bytes of byte-ordering swapped value to the designated address
*/

void tdsp_write_memory(struct tdsp_softc* sc, tdsp_addr_t addr, u_int32_t val)
{
       tdsp_reg32_t save;
       tdsp_addr_t offset;
       /* save DSPP to a temporary variable to restore later */
       save = dspp;
       /* set DSPP to the requested page and get the offset */
       offset = tdsp_set_page(sc, addr);
       /* change the byte ordering and write to the requested address */
       bus_space_write_4(sc->sc_pmemt, sc->sc_pmemh, offset, val);
       /* restore dspp */
       tdsp_set_page(sc, save << TDSP_PAGE_WIDTH);
}

/*
* Read a block of DSP memory to CPU memory.
* Return the number of bytes read.
*/

int tdsp_read_memory_block(struct tdsp_softc *sc, const tdsp_addr_t tdsp_addr,
    u_int8_t *datap, u_int32_t count)
{
       bus_size_t offset;
       /* save DSPP to restore later */
       tdsp_reg32_t save = dspp;
       /* check if the size of the chunk is valid */
       if((tdsp_addr >> TDSP_PAGE_WIDTH) !=
           ((tdsp_addr + count - 1) >> TDSP_PAGE_WIDTH)){
               ERROR("%s: Memory block should be within a 4MB page.\n",
                   sc->sc_dev.dv_xname);
               return 0;
       }
       offset = tdsp_set_page(sc, tdsp_addr);
       bus_space_read_region_1(sc->sc_pmemt, sc->sc_pmemh, offset,
           datap, count);
       /* restore DSPP */
       tdsp_set_page(sc, save << TDSP_PAGE_WIDTH);
       return count;
}

/*
* Write a block of CPU memory to DSP memory.
* Return the number of bytes written.
*/

int tdsp_write_memory_block(struct tdsp_softc *sc, const tdsp_addr_t tdsp_addr,
  u_int8_t *datap, u_int32_t count)
{
       bus_size_t offset;
       /* save DSPP to restore later */
       tdsp_reg32_t save = dspp;
       /* check if the size of the chunk is valid */
       if((tdsp_addr >> TDSP_PAGE_WIDTH) !=
           ((tdsp_addr + count - 1) >> TDSP_PAGE_WIDTH)){
               ERROR("%s: Memory block should be within a 4MB block.\n",
                   sc->sc_dev.dv_xname);
               return 0;
       }
       offset = tdsp_set_page(sc, tdsp_addr);
       bus_space_write_region_1(sc->sc_pmemt, sc->sc_pmemh, offset,
           datap, count);
       /*
       {
               int i;
               for(i = 0; i < count; i++){
                       bus_space_write_1(sc->sc_pmemt, sc->sc_pmemh,
                           offset + i, *(datap + i));
               }
       }
       */
       /* restore DSPP */
       tdsp_set_page(sc, save << TDSP_PAGE_WIDTH);
       return count;
}

void tdsp_memory_test(struct tdsp_softc* sc, tdsp_addr_t start_addr,
   u_int32_t size)
{
       int addr;
       int step = 4;
       INFO("%s: Test memory from 0x%08x to 0x%08x",
           sc->sc_dev.dv_xname, start_addr, start_addr + size - 1);
       for(addr = start_addr; addr < start_addr + size; addr += step){
               if(addr % (size >> 4) == 0) INFO(".");
               tdsp_write_memory(sc, addr, addr);
               if(tdsp_read_memory(sc, addr) != addr){
                       ERROR("***Failed at 0x%08x***\n", addr);
                       return;
               }
       }
       INFO("OK.\n");
}
----
and test application program.
----
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include "../../sys/arch/sandpoint/xepro/tdsp_ioctl.h"

typedef struct mmapinfo {
       void *addr;
       u_int32_t page;
       u_int32_t offset;
       u_int32_t len;
} mmapinfo_t;

int print_memory(mmapinfo_t*);

#define printf(format, args...)         \
{                                               \
       fprintf(stdout, format, ##args);        \
}

#define BASE_ADDRESS 0x80800000

int
main(int argc, char* argv[])
{
       int tdsph;
       u_int32_t start_addr;
       u_int32_t end_addr;
       u_int8_t* addr;
       mmapinfo_t sram;
       start_addr = strtoul(argv[1], NULL, 16);
       end_addr = strtoul(argv[2], NULL, 16);

       if(argc < 3){
               printf("Input start address and end address as arguments.\n");
               return -1;
       }

       tdsph = open("/dev/tdsp", O_RDWR, 0);
       if(tdsph == -1){
               perror("open error");
               return -1;
       }

       sram.len = end_addr - start_addr + 1;
       printf("sram.len = 0x%x\n", sram.len);

       sram.offset = start_addr;
       if(ioctl(tdsph, TDSP_SET_PAGE, &sram.offset) == -1){
               perror("ioctl TDSP_SET_PAGE error");
               return -1;
       }
       printf("sram.offset = 0x%x\n", sram.offset);

       if(ioctl(tdsph, TDSP_GET_PAGE, &sram.page) == -1){
               perror("ioctl TDSP_GET_PAGE error");
               return -1;
       }
       printf("sram.page = 0x%x\n", sram.page);

       sram.addr = mmap(0, sram.len, PROT_READ, MAP_SHARED, tdsph,
           (off_t)sram.offset);
       if(sram.addr == MAP_FAILED){
               perror("mmap error");
               return -1;
       }
       printf("sram.addr = 0x%08x\n", (u_int32_t)sram.addr);

       print_memory(&sram);

       *((u_int32_t*)sram.addr) = 0x12345678;

       addr = (u_int8_t*)((BASE_ADDRESS + start_addr) % (1 << 22));
       if(ioctl(tdsph, TDSP_DISPLAY_MEMORY, &addr) == -1){
               perror("ioctl TDSP_DISPLAY_MEMORY error");
               return -1;
       }

       close(tdsph);

       return 0;
}
int
print_memory(mmapinfo_t *p_memory)
{
       int i = 0;
       u_int32_t start_addr = (p_memory->page << 22) + p_memory->offset;
       while(i < p_memory->len){
               if(i % 16 == 0)
                       printf("\n0x%08x: ", start_addr + i);
               printf("%02x ", *((u_int8_t*)p_memory->addr + i));
               i++;
       }
       printf("\n");
       return 0;
}
----