]> git.proxmox.com Git - qemu.git/blame - hw/omap_spi.c
Merge remote-tracking branch 'kraxel/usb.31' into staging
[qemu.git] / hw / omap_spi.c
CommitLineData
2d08cc7c 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 *)
7267c094 318 g_malloc0(sizeof(struct omap_mcspi_s));
2d08cc7c 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}