summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.target2
-rw-r--r--hw/omap.h2
-rw-r--r--hw/omap2.c323
-rw-r--r--hw/omap_spi.c346
4 files changed, 349 insertions, 324 deletions
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 <andrew@openedhand.com>
+ *
+ * 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;
+}