From 2d08cc7c3fed692fc44615e31527450be1363691 Mon Sep 17 00:00:00 2001 From: cmchao Date: Mon, 31 May 2010 23:54:20 +0800 Subject: [PATCH] hw/omap2.c : separate spi module Signed-off-by: cmchao Signed-off-by: Aurelien Jarno --- Makefile.target | 2 +- hw/omap.h | 2 + hw/omap2.c | 323 -------------------------------------------- hw/omap_spi.c | 346 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 349 insertions(+), 324 deletions(-) create mode 100644 hw/omap_spi.c diff --git a/Makefile.target b/Makefile.target index c909680ad2..cd635f14f4 100644 --- a/Makefile.target +++ b/Makefile.target @@ -269,7 +269,7 @@ obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o obj-arm-y += gumstix.o obj-arm-y += zaurus.o ide/microdrive.o spitz.o tosa.o tc6393xb.o obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o omap_gpio.o omap_intc.o -obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o omap_gpmc.o omap_sdrc.o +obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o omap_gpmc.o omap_sdrc.o omap_spi.o obj-arm-y += omap_sx1.o palm.o tsc210x.o obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o obj-arm-y += mst_fpga.o mainstone.o diff --git a/hw/omap.h b/hw/omap.h index ea23ec97b8..fef495af0c 100644 --- a/hw/omap.h +++ b/hw/omap.h @@ -706,12 +706,14 @@ struct omap_uwire_s *omap_uwire_init(target_phys_addr_t base, void omap_uwire_attach(struct omap_uwire_s *s, uWireSlave *slave, int chipselect); +/* OMAP2 spi */ struct omap_mcspi_s; struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum, qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk); void omap_mcspi_attach(struct omap_mcspi_s *s, uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque, int chipselect); +void omap_mcspi_reset(struct omap_mcspi_s *s); struct omap_rtc_s; struct omap_rtc_s *omap_rtc_init(target_phys_addr_t base, diff --git a/hw/omap2.c b/hw/omap2.c index 0e78fc5062..47a3812045 100644 --- a/hw/omap2.c +++ b/hw/omap2.c @@ -29,329 +29,6 @@ #include "soc_dma.h" #include "audio/audio.h" -/* Multichannel SPI */ -struct omap_mcspi_s { - qemu_irq irq; - int chnum; - - uint32_t sysconfig; - uint32_t systest; - uint32_t irqst; - uint32_t irqen; - uint32_t wken; - uint32_t control; - - struct omap_mcspi_ch_s { - qemu_irq txdrq; - qemu_irq rxdrq; - uint32_t (*txrx)(void *opaque, uint32_t, int); - void *opaque; - - uint32_t tx; - uint32_t rx; - - uint32_t config; - uint32_t status; - uint32_t control; - } ch[4]; -}; - -static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s) -{ - qemu_set_irq(s->irq, s->irqst & s->irqen); -} - -static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch) -{ - qemu_set_irq(ch->txdrq, - (ch->control & 1) && /* EN */ - (ch->config & (1 << 14)) && /* DMAW */ - (ch->status & (1 << 1)) && /* TXS */ - ((ch->config >> 12) & 3) != 1); /* TRM */ - qemu_set_irq(ch->rxdrq, - (ch->control & 1) && /* EN */ - (ch->config & (1 << 15)) && /* DMAW */ - (ch->status & (1 << 0)) && /* RXS */ - ((ch->config >> 12) & 3) != 2); /* TRM */ -} - -static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum) -{ - struct omap_mcspi_ch_s *ch = s->ch + chnum; - - if (!(ch->control & 1)) /* EN */ - return; - if ((ch->status & (1 << 0)) && /* RXS */ - ((ch->config >> 12) & 3) != 2 && /* TRM */ - !(ch->config & (1 << 19))) /* TURBO */ - goto intr_update; - if ((ch->status & (1 << 1)) && /* TXS */ - ((ch->config >> 12) & 3) != 1) /* TRM */ - goto intr_update; - - if (!(s->control & 1) || /* SINGLE */ - (ch->config & (1 << 20))) { /* FORCE */ - if (ch->txrx) - ch->rx = ch->txrx(ch->opaque, ch->tx, /* WL */ - 1 + (0x1f & (ch->config >> 7))); - } - - ch->tx = 0; - ch->status |= 1 << 2; /* EOT */ - ch->status |= 1 << 1; /* TXS */ - if (((ch->config >> 12) & 3) != 2) /* TRM */ - ch->status |= 1 << 0; /* RXS */ - -intr_update: - if ((ch->status & (1 << 0)) && /* RXS */ - ((ch->config >> 12) & 3) != 2 && /* TRM */ - !(ch->config & (1 << 19))) /* TURBO */ - s->irqst |= 1 << (2 + 4 * chnum); /* RX_FULL */ - if ((ch->status & (1 << 1)) && /* TXS */ - ((ch->config >> 12) & 3) != 1) /* TRM */ - s->irqst |= 1 << (0 + 4 * chnum); /* TX_EMPTY */ - omap_mcspi_interrupt_update(s); - omap_mcspi_dmarequest_update(ch); -} - -static void omap_mcspi_reset(struct omap_mcspi_s *s) -{ - int ch; - - s->sysconfig = 0; - s->systest = 0; - s->irqst = 0; - s->irqen = 0; - s->wken = 0; - s->control = 4; - - for (ch = 0; ch < 4; ch ++) { - s->ch[ch].config = 0x060000; - s->ch[ch].status = 2; /* TXS */ - s->ch[ch].control = 0; - - omap_mcspi_dmarequest_update(s->ch + ch); - } - - omap_mcspi_interrupt_update(s); -} - -static uint32_t omap_mcspi_read(void *opaque, target_phys_addr_t addr) -{ - struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; - int ch = 0; - uint32_t ret; - - switch (addr) { - case 0x00: /* MCSPI_REVISION */ - return 0x91; - - case 0x10: /* MCSPI_SYSCONFIG */ - return s->sysconfig; - - case 0x14: /* MCSPI_SYSSTATUS */ - return 1; /* RESETDONE */ - - case 0x18: /* MCSPI_IRQSTATUS */ - return s->irqst; - - case 0x1c: /* MCSPI_IRQENABLE */ - return s->irqen; - - case 0x20: /* MCSPI_WAKEUPENABLE */ - return s->wken; - - case 0x24: /* MCSPI_SYST */ - return s->systest; - - case 0x28: /* MCSPI_MODULCTRL */ - return s->control; - - case 0x68: ch ++; - case 0x54: ch ++; - case 0x40: ch ++; - case 0x2c: /* MCSPI_CHCONF */ - return s->ch[ch].config; - - case 0x6c: ch ++; - case 0x58: ch ++; - case 0x44: ch ++; - case 0x30: /* MCSPI_CHSTAT */ - return s->ch[ch].status; - - case 0x70: ch ++; - case 0x5c: ch ++; - case 0x48: ch ++; - case 0x34: /* MCSPI_CHCTRL */ - return s->ch[ch].control; - - case 0x74: ch ++; - case 0x60: ch ++; - case 0x4c: ch ++; - case 0x38: /* MCSPI_TX */ - return s->ch[ch].tx; - - case 0x78: ch ++; - case 0x64: ch ++; - case 0x50: ch ++; - case 0x3c: /* MCSPI_RX */ - s->ch[ch].status &= ~(1 << 0); /* RXS */ - ret = s->ch[ch].rx; - omap_mcspi_transfer_run(s, ch); - return ret; - } - - OMAP_BAD_REG(addr); - return 0; -} - -static void omap_mcspi_write(void *opaque, target_phys_addr_t addr, - uint32_t value) -{ - struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; - int ch = 0; - - switch (addr) { - case 0x00: /* MCSPI_REVISION */ - case 0x14: /* MCSPI_SYSSTATUS */ - case 0x30: /* MCSPI_CHSTAT0 */ - case 0x3c: /* MCSPI_RX0 */ - case 0x44: /* MCSPI_CHSTAT1 */ - case 0x50: /* MCSPI_RX1 */ - case 0x58: /* MCSPI_CHSTAT2 */ - case 0x64: /* MCSPI_RX2 */ - case 0x6c: /* MCSPI_CHSTAT3 */ - case 0x78: /* MCSPI_RX3 */ - OMAP_RO_REG(addr); - return; - - case 0x10: /* MCSPI_SYSCONFIG */ - if (value & (1 << 1)) /* SOFTRESET */ - omap_mcspi_reset(s); - s->sysconfig = value & 0x31d; - break; - - case 0x18: /* MCSPI_IRQSTATUS */ - if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) { - s->irqst &= ~value; - omap_mcspi_interrupt_update(s); - } - break; - - case 0x1c: /* MCSPI_IRQENABLE */ - s->irqen = value & 0x1777f; - omap_mcspi_interrupt_update(s); - break; - - case 0x20: /* MCSPI_WAKEUPENABLE */ - s->wken = value & 1; - break; - - case 0x24: /* MCSPI_SYST */ - if (s->control & (1 << 3)) /* SYSTEM_TEST */ - if (value & (1 << 11)) { /* SSB */ - s->irqst |= 0x1777f; - omap_mcspi_interrupt_update(s); - } - s->systest = value & 0xfff; - break; - - case 0x28: /* MCSPI_MODULCTRL */ - if (value & (1 << 3)) /* SYSTEM_TEST */ - if (s->systest & (1 << 11)) { /* SSB */ - s->irqst |= 0x1777f; - omap_mcspi_interrupt_update(s); - } - s->control = value & 0xf; - break; - - case 0x68: ch ++; - case 0x54: ch ++; - case 0x40: ch ++; - case 0x2c: /* MCSPI_CHCONF */ - if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */ - omap_mcspi_dmarequest_update(s->ch + ch); - if (((value >> 12) & 3) == 3) /* TRM */ - fprintf(stderr, "%s: invalid TRM value (3)\n", __FUNCTION__); - if (((value >> 7) & 0x1f) < 3) /* WL */ - fprintf(stderr, "%s: invalid WL value (%i)\n", - __FUNCTION__, (value >> 7) & 0x1f); - s->ch[ch].config = value & 0x7fffff; - break; - - case 0x70: ch ++; - case 0x5c: ch ++; - case 0x48: ch ++; - case 0x34: /* MCSPI_CHCTRL */ - if (value & ~s->ch[ch].control & 1) { /* EN */ - s->ch[ch].control |= 1; - omap_mcspi_transfer_run(s, ch); - } else - s->ch[ch].control = value & 1; - break; - - case 0x74: ch ++; - case 0x60: ch ++; - case 0x4c: ch ++; - case 0x38: /* MCSPI_TX */ - s->ch[ch].tx = value; - s->ch[ch].status &= ~(1 << 1); /* TXS */ - omap_mcspi_transfer_run(s, ch); - break; - - default: - OMAP_BAD_REG(addr); - return; - } -} - -static CPUReadMemoryFunc * const omap_mcspi_readfn[] = { - omap_badwidth_read32, - omap_badwidth_read32, - omap_mcspi_read, -}; - -static CPUWriteMemoryFunc * const omap_mcspi_writefn[] = { - omap_badwidth_write32, - omap_badwidth_write32, - omap_mcspi_write, -}; - -struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum, - qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk) -{ - int iomemtype; - struct omap_mcspi_s *s = (struct omap_mcspi_s *) - qemu_mallocz(sizeof(struct omap_mcspi_s)); - struct omap_mcspi_ch_s *ch = s->ch; - - s->irq = irq; - s->chnum = chnum; - while (chnum --) { - ch->txdrq = *drq ++; - ch->rxdrq = *drq ++; - ch ++; - } - omap_mcspi_reset(s); - - iomemtype = l4_register_io_memory(omap_mcspi_readfn, - omap_mcspi_writefn, s); - omap_l4_attach(ta, 0, iomemtype); - - return s; -} - -void omap_mcspi_attach(struct omap_mcspi_s *s, - uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque, - int chipselect) -{ - if (chipselect < 0 || chipselect >= s->chnum) - hw_error("%s: Bad chipselect %i\n", __FUNCTION__, chipselect); - - s->ch[chipselect].txrx = txrx; - s->ch[chipselect].opaque = opaque; -} - /* Enhanced Audio Controller (CODEC only) */ struct omap_eac_s { qemu_irq irq; diff --git a/hw/omap_spi.c b/hw/omap_spi.c new file mode 100644 index 0000000000..a6b03496c4 --- /dev/null +++ b/hw/omap_spi.c @@ -0,0 +1,346 @@ +/* + * TI OMAP processor's Multichannel SPI emulation. + * + * Copyright (C) 2007-2009 Nokia Corporation + * + * Original code for OMAP2 by Andrzej Zaborowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "hw.h" +#include "omap.h" + +/* Multichannel SPI */ +struct omap_mcspi_s { + qemu_irq irq; + int chnum; + + uint32_t sysconfig; + uint32_t systest; + uint32_t irqst; + uint32_t irqen; + uint32_t wken; + uint32_t control; + + struct omap_mcspi_ch_s { + qemu_irq txdrq; + qemu_irq rxdrq; + uint32_t (*txrx)(void *opaque, uint32_t, int); + void *opaque; + + uint32_t tx; + uint32_t rx; + + uint32_t config; + uint32_t status; + uint32_t control; + } ch[4]; +}; + +static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s) +{ + qemu_set_irq(s->irq, s->irqst & s->irqen); +} + +static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch) +{ + qemu_set_irq(ch->txdrq, + (ch->control & 1) && /* EN */ + (ch->config & (1 << 14)) && /* DMAW */ + (ch->status & (1 << 1)) && /* TXS */ + ((ch->config >> 12) & 3) != 1); /* TRM */ + qemu_set_irq(ch->rxdrq, + (ch->control & 1) && /* EN */ + (ch->config & (1 << 15)) && /* DMAW */ + (ch->status & (1 << 0)) && /* RXS */ + ((ch->config >> 12) & 3) != 2); /* TRM */ +} + +static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum) +{ + struct omap_mcspi_ch_s *ch = s->ch + chnum; + + if (!(ch->control & 1)) /* EN */ + return; + if ((ch->status & (1 << 0)) && /* RXS */ + ((ch->config >> 12) & 3) != 2 && /* TRM */ + !(ch->config & (1 << 19))) /* TURBO */ + goto intr_update; + if ((ch->status & (1 << 1)) && /* TXS */ + ((ch->config >> 12) & 3) != 1) /* TRM */ + goto intr_update; + + if (!(s->control & 1) || /* SINGLE */ + (ch->config & (1 << 20))) { /* FORCE */ + if (ch->txrx) + ch->rx = ch->txrx(ch->opaque, ch->tx, /* WL */ + 1 + (0x1f & (ch->config >> 7))); + } + + ch->tx = 0; + ch->status |= 1 << 2; /* EOT */ + ch->status |= 1 << 1; /* TXS */ + if (((ch->config >> 12) & 3) != 2) /* TRM */ + ch->status |= 1 << 0; /* RXS */ + +intr_update: + if ((ch->status & (1 << 0)) && /* RXS */ + ((ch->config >> 12) & 3) != 2 && /* TRM */ + !(ch->config & (1 << 19))) /* TURBO */ + s->irqst |= 1 << (2 + 4 * chnum); /* RX_FULL */ + if ((ch->status & (1 << 1)) && /* TXS */ + ((ch->config >> 12) & 3) != 1) /* TRM */ + s->irqst |= 1 << (0 + 4 * chnum); /* TX_EMPTY */ + omap_mcspi_interrupt_update(s); + omap_mcspi_dmarequest_update(ch); +} + +void omap_mcspi_reset(struct omap_mcspi_s *s) +{ + int ch; + + s->sysconfig = 0; + s->systest = 0; + s->irqst = 0; + s->irqen = 0; + s->wken = 0; + s->control = 4; + + for (ch = 0; ch < 4; ch ++) { + s->ch[ch].config = 0x060000; + s->ch[ch].status = 2; /* TXS */ + s->ch[ch].control = 0; + + omap_mcspi_dmarequest_update(s->ch + ch); + } + + omap_mcspi_interrupt_update(s); +} + +static uint32_t omap_mcspi_read(void *opaque, target_phys_addr_t addr) +{ + struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; + int ch = 0; + uint32_t ret; + + switch (addr) { + case 0x00: /* MCSPI_REVISION */ + return 0x91; + + case 0x10: /* MCSPI_SYSCONFIG */ + return s->sysconfig; + + case 0x14: /* MCSPI_SYSSTATUS */ + return 1; /* RESETDONE */ + + case 0x18: /* MCSPI_IRQSTATUS */ + return s->irqst; + + case 0x1c: /* MCSPI_IRQENABLE */ + return s->irqen; + + case 0x20: /* MCSPI_WAKEUPENABLE */ + return s->wken; + + case 0x24: /* MCSPI_SYST */ + return s->systest; + + case 0x28: /* MCSPI_MODULCTRL */ + return s->control; + + case 0x68: ch ++; + case 0x54: ch ++; + case 0x40: ch ++; + case 0x2c: /* MCSPI_CHCONF */ + return s->ch[ch].config; + + case 0x6c: ch ++; + case 0x58: ch ++; + case 0x44: ch ++; + case 0x30: /* MCSPI_CHSTAT */ + return s->ch[ch].status; + + case 0x70: ch ++; + case 0x5c: ch ++; + case 0x48: ch ++; + case 0x34: /* MCSPI_CHCTRL */ + return s->ch[ch].control; + + case 0x74: ch ++; + case 0x60: ch ++; + case 0x4c: ch ++; + case 0x38: /* MCSPI_TX */ + return s->ch[ch].tx; + + case 0x78: ch ++; + case 0x64: ch ++; + case 0x50: ch ++; + case 0x3c: /* MCSPI_RX */ + s->ch[ch].status &= ~(1 << 0); /* RXS */ + ret = s->ch[ch].rx; + omap_mcspi_transfer_run(s, ch); + return ret; + } + + OMAP_BAD_REG(addr); + return 0; +} + +static void omap_mcspi_write(void *opaque, target_phys_addr_t addr, + uint32_t value) +{ + struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque; + int ch = 0; + + switch (addr) { + case 0x00: /* MCSPI_REVISION */ + case 0x14: /* MCSPI_SYSSTATUS */ + case 0x30: /* MCSPI_CHSTAT0 */ + case 0x3c: /* MCSPI_RX0 */ + case 0x44: /* MCSPI_CHSTAT1 */ + case 0x50: /* MCSPI_RX1 */ + case 0x58: /* MCSPI_CHSTAT2 */ + case 0x64: /* MCSPI_RX2 */ + case 0x6c: /* MCSPI_CHSTAT3 */ + case 0x78: /* MCSPI_RX3 */ + OMAP_RO_REG(addr); + return; + + case 0x10: /* MCSPI_SYSCONFIG */ + if (value & (1 << 1)) /* SOFTRESET */ + omap_mcspi_reset(s); + s->sysconfig = value & 0x31d; + break; + + case 0x18: /* MCSPI_IRQSTATUS */ + if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) { + s->irqst &= ~value; + omap_mcspi_interrupt_update(s); + } + break; + + case 0x1c: /* MCSPI_IRQENABLE */ + s->irqen = value & 0x1777f; + omap_mcspi_interrupt_update(s); + break; + + case 0x20: /* MCSPI_WAKEUPENABLE */ + s->wken = value & 1; + break; + + case 0x24: /* MCSPI_SYST */ + if (s->control & (1 << 3)) /* SYSTEM_TEST */ + if (value & (1 << 11)) { /* SSB */ + s->irqst |= 0x1777f; + omap_mcspi_interrupt_update(s); + } + s->systest = value & 0xfff; + break; + + case 0x28: /* MCSPI_MODULCTRL */ + if (value & (1 << 3)) /* SYSTEM_TEST */ + if (s->systest & (1 << 11)) { /* SSB */ + s->irqst |= 0x1777f; + omap_mcspi_interrupt_update(s); + } + s->control = value & 0xf; + break; + + case 0x68: ch ++; + case 0x54: ch ++; + case 0x40: ch ++; + case 0x2c: /* MCSPI_CHCONF */ + if ((value ^ s->ch[ch].config) & (3 << 14)) /* DMAR | DMAW */ + omap_mcspi_dmarequest_update(s->ch + ch); + if (((value >> 12) & 3) == 3) /* TRM */ + fprintf(stderr, "%s: invalid TRM value (3)\n", __FUNCTION__); + if (((value >> 7) & 0x1f) < 3) /* WL */ + fprintf(stderr, "%s: invalid WL value (%i)\n", + __FUNCTION__, (value >> 7) & 0x1f); + s->ch[ch].config = value & 0x7fffff; + break; + + case 0x70: ch ++; + case 0x5c: ch ++; + case 0x48: ch ++; + case 0x34: /* MCSPI_CHCTRL */ + if (value & ~s->ch[ch].control & 1) { /* EN */ + s->ch[ch].control |= 1; + omap_mcspi_transfer_run(s, ch); + } else + s->ch[ch].control = value & 1; + break; + + case 0x74: ch ++; + case 0x60: ch ++; + case 0x4c: ch ++; + case 0x38: /* MCSPI_TX */ + s->ch[ch].tx = value; + s->ch[ch].status &= ~(1 << 1); /* TXS */ + omap_mcspi_transfer_run(s, ch); + break; + + default: + OMAP_BAD_REG(addr); + return; + } +} + +static CPUReadMemoryFunc * const omap_mcspi_readfn[] = { + omap_badwidth_read32, + omap_badwidth_read32, + omap_mcspi_read, +}; + +static CPUWriteMemoryFunc * const omap_mcspi_writefn[] = { + omap_badwidth_write32, + omap_badwidth_write32, + omap_mcspi_write, +}; + +struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum, + qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk) +{ + int iomemtype; + struct omap_mcspi_s *s = (struct omap_mcspi_s *) + qemu_mallocz(sizeof(struct omap_mcspi_s)); + struct omap_mcspi_ch_s *ch = s->ch; + + s->irq = irq; + s->chnum = chnum; + while (chnum --) { + ch->txdrq = *drq ++; + ch->rxdrq = *drq ++; + ch ++; + } + omap_mcspi_reset(s); + + iomemtype = l4_register_io_memory(omap_mcspi_readfn, + omap_mcspi_writefn, s); + omap_l4_attach(ta, 0, iomemtype); + + return s; +} + +void omap_mcspi_attach(struct omap_mcspi_s *s, + uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque, + int chipselect) +{ + if (chipselect < 0 || chipselect >= s->chnum) + hw_error("%s: Bad chipselect %i\n", __FUNCTION__, chipselect); + + s->ch[chipselect].txrx = txrx; + s->ch[chipselect].opaque = opaque; +}