]> Git Repo - qemu.git/blob - hw/omap_spi.c
pcnet: Fix sign extension: make ipxe work with >2G RAM
[qemu.git] / hw / omap_spi.c
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 <[email protected]>
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 */
26 struct 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
52 static inline void omap_mcspi_interrupt_update(struct omap_mcspi_s *s)
53 {
54     qemu_set_irq(s->irq, s->irqst & s->irqen);
55 }
56
57 static 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
71 static 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
98 intr_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
110 void 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
132 static 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
201 static 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
301 static CPUReadMemoryFunc * const omap_mcspi_readfn[] = {
302     omap_badwidth_read32,
303     omap_badwidth_read32,
304     omap_mcspi_read,
305 };
306
307 static CPUWriteMemoryFunc * const omap_mcspi_writefn[] = {
308     omap_badwidth_write32,
309     omap_badwidth_write32,
310     omap_mcspi_write,
311 };
312
313 struct 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
337 void 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 }
This page took 0.047598 seconds and 4 git commands to generate.