Statistics
| Branch: | Revision:

root / hw / omap_spi.c @ 37952117

History | View | Annotate | Download (9.7 kB)

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