qemu/hw/omap_spi.c
<<
>>
Prefs
   1/*
   2 * TI OMAP processor's Multichannel SPI emulation.
   3 *
   4 * Copyright (C) 2007-2009 Nokia Corporation
   5 *
   6 * Original code for OMAP2 by Andrzej Zaborowski <andrew@openedhand.com>
   7 *
   8 * This program is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU General Public License as
  10 * published by the Free Software Foundation; either version 2 or
  11 * (at your option) any later version of the License.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License along
  19 * with this program; if not, write to the Free Software Foundation, Inc.,
  20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21 */
  22#include "hw.h"
  23#include "omap.h"
  24
  25/* Multichannel SPI */
  26struct omap_mcspi_s {
  27    qemu_irq irq;
  28    int chnum;
  29
  30    uint32_t sysconfig;
  31    uint32_t systest;
  32    uint32_t irqst;
  33    uint32_t irqen;
  34    uint32_t wken;
  35    uint32_t control;
  36
  37    struct omap_mcspi_ch_s {
  38        qemu_irq txdrq;
  39        qemu_irq rxdrq;
  40        uint32_t (*txrx)(void *opaque, uint32_t, int);
  41        void *opaque;
  42
  43        uint32_t tx;
  44        uint32_t rx;
  45
  46        uint32_t config;
  47        uint32_t status;
  48        uint32_t control;
  49    } ch[4];
  50};
  51
  52static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s)
  53{
  54    qemu_set_irq(s->irq, s->irqst & s->irqen);
  55}
  56
  57static inline void omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s *ch)
  58{
  59    qemu_set_irq(ch->txdrq,
  60                    (ch->control & 1) &&                /* EN */
  61                    (ch->config & (1 << 14)) &&         /* DMAW */
  62                    (ch->status & (1 << 1)) &&          /* TXS */
  63                    ((ch->config >> 12) & 3) != 1);     /* TRM */
  64    qemu_set_irq(ch->rxdrq,
  65                    (ch->control & 1) &&                /* EN */
  66                    (ch->config & (1 << 15)) &&         /* DMAW */
  67                    (ch->status & (1 << 0)) &&          /* RXS */
  68                    ((ch->config >> 12) & 3) != 2);     /* TRM */
  69}
  70
  71static void omap_mcspi_transfer_run(struct omap_mcspi_s *s, int chnum)
  72{
  73    struct omap_mcspi_ch_s *ch = s->ch + chnum;
  74
  75    if (!(ch->control & 1))                             /* EN */
  76        return;
  77    if ((ch->status & (1 << 0)) &&                      /* RXS */
  78                    ((ch->config >> 12) & 3) != 2 &&    /* TRM */
  79                    !(ch->config & (1 << 19)))          /* TURBO */
  80        goto intr_update;
  81    if ((ch->status & (1 << 1)) &&                      /* TXS */
  82                    ((ch->config >> 12) & 3) != 1)      /* TRM */
  83        goto intr_update;
  84
  85    if (!(s->control & 1) ||                            /* SINGLE */
  86                    (ch->config & (1 << 20))) {         /* FORCE */
  87        if (ch->txrx)
  88            ch->rx = ch->txrx(ch->opaque, ch->tx,       /* WL */
  89                            1 + (0x1f & (ch->config >> 7)));
  90    }
  91
  92    ch->tx = 0;
  93    ch->status |= 1 << 2;                               /* EOT */
  94    ch->status |= 1 << 1;                               /* TXS */
  95    if (((ch->config >> 12) & 3) != 2)                  /* TRM */
  96        ch->status |= 1 << 0;                           /* RXS */
  97
  98intr_update:
  99    if ((ch->status & (1 << 0)) &&                      /* RXS */
 100                    ((ch->config >> 12) & 3) != 2 &&    /* TRM */
 101                    !(ch->config & (1 << 19)))          /* TURBO */
 102        s->irqst |= 1 << (2 + 4 * chnum);               /* RX_FULL */
 103    if ((ch->status & (1 << 1)) &&                      /* TXS */
 104                    ((ch->config >> 12) & 3) != 1)      /* TRM */
 105        s->irqst |= 1 << (0 + 4 * chnum);               /* TX_EMPTY */
 106    omap_mcspi_interrupt_update(s);
 107    omap_mcspi_dmarequest_update(ch);
 108}
 109
 110void omap_mcspi_reset(struct omap_mcspi_s *s)
 111{
 112    int ch;
 113
 114    s->sysconfig = 0;
 115    s->systest = 0;
 116    s->irqst = 0;
 117    s->irqen = 0;
 118    s->wken = 0;
 119    s->control = 4;
 120
 121    for (ch = 0; ch < 4; ch ++) {
 122        s->ch[ch].config = 0x060000;
 123        s->ch[ch].status = 2;                           /* TXS */
 124        s->ch[ch].control = 0;
 125
 126        omap_mcspi_dmarequest_update(s->ch + ch);
 127    }
 128
 129    omap_mcspi_interrupt_update(s);
 130}
 131
 132static uint32_t omap_mcspi_read(void *opaque, target_phys_addr_t addr)
 133{
 134    struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
 135    int ch = 0;
 136    uint32_t ret;
 137
 138    switch (addr) {
 139    case 0x00:  /* MCSPI_REVISION */
 140        return 0x91;
 141
 142    case 0x10:  /* MCSPI_SYSCONFIG */
 143        return s->sysconfig;
 144
 145    case 0x14:  /* MCSPI_SYSSTATUS */
 146        return 1;                                       /* RESETDONE */
 147
 148    case 0x18:  /* MCSPI_IRQSTATUS */
 149        return s->irqst;
 150
 151    case 0x1c:  /* MCSPI_IRQENABLE */
 152        return s->irqen;
 153
 154    case 0x20:  /* MCSPI_WAKEUPENABLE */
 155        return s->wken;
 156
 157    case 0x24:  /* MCSPI_SYST */
 158        return s->systest;
 159
 160    case 0x28:  /* MCSPI_MODULCTRL */
 161        return s->control;
 162
 163    case 0x68: ch ++;
 164    case 0x54: ch ++;
 165    case 0x40: ch ++;
 166    case 0x2c:  /* MCSPI_CHCONF */
 167        return s->ch[ch].config;
 168
 169    case 0x6c: ch ++;
 170    case 0x58: ch ++;
 171    case 0x44: ch ++;
 172    case 0x30:  /* MCSPI_CHSTAT */
 173        return s->ch[ch].status;
 174
 175    case 0x70: ch ++;
 176    case 0x5c: ch ++;
 177    case 0x48: ch ++;
 178    case 0x34:  /* MCSPI_CHCTRL */
 179        return s->ch[ch].control;
 180
 181    case 0x74: ch ++;
 182    case 0x60: ch ++;
 183    case 0x4c: ch ++;
 184    case 0x38:  /* MCSPI_TX */
 185        return s->ch[ch].tx;
 186
 187    case 0x78: ch ++;
 188    case 0x64: ch ++;
 189    case 0x50: ch ++;
 190    case 0x3c:  /* MCSPI_RX */
 191        s->ch[ch].status &= ~(1 << 0);                  /* RXS */
 192        ret = s->ch[ch].rx;
 193        omap_mcspi_transfer_run(s, ch);
 194        return ret;
 195    }
 196
 197    OMAP_BAD_REG(addr);
 198    return 0;
 199}
 200
 201static void omap_mcspi_write(void *opaque, target_phys_addr_t addr,
 202                uint32_t value)
 203{
 204    struct omap_mcspi_s *s = (struct omap_mcspi_s *) opaque;
 205    int ch = 0;
 206
 207    switch (addr) {
 208    case 0x00:  /* MCSPI_REVISION */
 209    case 0x14:  /* MCSPI_SYSSTATUS */
 210    case 0x30:  /* MCSPI_CHSTAT0 */
 211    case 0x3c:  /* MCSPI_RX0 */
 212    case 0x44:  /* MCSPI_CHSTAT1 */
 213    case 0x50:  /* MCSPI_RX1 */
 214    case 0x58:  /* MCSPI_CHSTAT2 */
 215    case 0x64:  /* MCSPI_RX2 */
 216    case 0x6c:  /* MCSPI_CHSTAT3 */
 217    case 0x78:  /* MCSPI_RX3 */
 218        OMAP_RO_REG(addr);
 219        return;
 220
 221    case 0x10:  /* MCSPI_SYSCONFIG */
 222        if (value & (1 << 1))                           /* SOFTRESET */
 223            omap_mcspi_reset(s);
 224        s->sysconfig = value & 0x31d;
 225        break;
 226
 227    case 0x18:  /* MCSPI_IRQSTATUS */
 228        if (!((s->control & (1 << 3)) && (s->systest & (1 << 11)))) {
 229            s->irqst &= ~value;
 230            omap_mcspi_interrupt_update(s);
 231        }
 232        break;
 233
 234    case 0x1c:  /* MCSPI_IRQENABLE */
 235        s->irqen = value & 0x1777f;
 236        omap_mcspi_interrupt_update(s);
 237        break;
 238
 239    case 0x20:  /* MCSPI_WAKEUPENABLE */
 240        s->wken = value & 1;
 241        break;
 242
 243    case 0x24:  /* MCSPI_SYST */
 244        if (s->control & (1 << 3))                      /* SYSTEM_TEST */
 245            if (value & (1 << 11)) {                    /* SSB */
 246                s->irqst |= 0x1777f;
 247                omap_mcspi_interrupt_update(s);
 248            }
 249        s->systest = value & 0xfff;
 250        break;
 251
 252    case 0x28:  /* MCSPI_MODULCTRL */
 253        if (value & (1 << 3))                           /* SYSTEM_TEST */
 254            if (s->systest & (1 << 11)) {               /* SSB */
 255                s->irqst |= 0x1777f;
 256                omap_mcspi_interrupt_update(s);
 257            }
 258        s->control = value & 0xf;
 259        break;
 260
 261    case 0x68: ch ++;
 262    case 0x54: ch ++;
 263    case 0x40: ch ++;
 264    case 0x2c:  /* MCSPI_CHCONF */
 265        if ((value ^ s->ch[ch].config) & (3 << 14))     /* DMAR | DMAW */
 266            omap_mcspi_dmarequest_update(s->ch + ch);
 267        if (((value >> 12) & 3) == 3)                   /* TRM */
 268            fprintf(stderr, "%s: invalid TRM value (3)\n", __FUNCTION__);
 269        if (((value >> 7) & 0x1f) < 3)                  /* WL */
 270            fprintf(stderr, "%s: invalid WL value (%i)\n",
 271                            __FUNCTION__, (value >> 7) & 0x1f);
 272        s->ch[ch].config = value & 0x7fffff;
 273        break;
 274
 275    case 0x70: ch ++;
 276    case 0x5c: ch ++;
 277    case 0x48: ch ++;
 278    case 0x34:  /* MCSPI_CHCTRL */
 279        if (value & ~s->ch[ch].control & 1) {           /* EN */
 280            s->ch[ch].control |= 1;
 281            omap_mcspi_transfer_run(s, ch);
 282        } else
 283            s->ch[ch].control = value & 1;
 284        break;
 285
 286    case 0x74: ch ++;
 287    case 0x60: ch ++;
 288    case 0x4c: ch ++;
 289    case 0x38:  /* MCSPI_TX */
 290        s->ch[ch].tx = value;
 291        s->ch[ch].status &= ~(1 << 1);                  /* TXS */
 292        omap_mcspi_transfer_run(s, ch);
 293        break;
 294
 295    default:
 296        OMAP_BAD_REG(addr);
 297        return;
 298    }
 299}
 300
 301static CPUReadMemoryFunc * const omap_mcspi_readfn[] = {
 302    omap_badwidth_read32,
 303    omap_badwidth_read32,
 304    omap_mcspi_read,
 305};
 306
 307static CPUWriteMemoryFunc * const omap_mcspi_writefn[] = {
 308    omap_badwidth_write32,
 309    omap_badwidth_write32,
 310    omap_mcspi_write,
 311};
 312
 313struct omap_mcspi_s *omap_mcspi_init(struct omap_target_agent_s *ta, int chnum,
 314                qemu_irq irq, qemu_irq *drq, omap_clk fclk, omap_clk iclk)
 315{
 316    int iomemtype;
 317    struct omap_mcspi_s *s = (struct omap_mcspi_s *)
 318            qemu_mallocz(sizeof(struct omap_mcspi_s));
 319    struct omap_mcspi_ch_s *ch = s->ch;
 320
 321    s->irq = irq;
 322    s->chnum = chnum;
 323    while (chnum --) {
 324        ch->txdrq = *drq ++;
 325        ch->rxdrq = *drq ++;
 326        ch ++;
 327    }
 328    omap_mcspi_reset(s);
 329
 330    iomemtype = l4_register_io_memory(omap_mcspi_readfn,
 331                    omap_mcspi_writefn, s);
 332    omap_l4_attach(ta, 0, iomemtype);
 333
 334    return s;
 335}
 336
 337void omap_mcspi_attach(struct omap_mcspi_s *s,
 338                uint32_t (*txrx)(void *opaque, uint32_t, int), void *opaque,
 339                int chipselect)
 340{
 341    if (chipselect < 0 || chipselect >= s->chnum)
 342        hw_error("%s: Bad chipselect %i\n", __FUNCTION__, chipselect);
 343
 344    s->ch[chipselect].txrx = txrx;
 345    s->ch[chipselect].opaque = opaque;
 346}
 347