xref: /illumos-kvm-cmd/hw/omap_spi.c (revision 68396ea9)
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 */
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 
omap_mcspi_interrupt_update(struct omap_mcspi_s * s)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 
omap_mcspi_dmarequest_update(struct omap_mcspi_ch_s * ch)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 
omap_mcspi_transfer_run(struct omap_mcspi_s * s,int chnum)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 
omap_mcspi_reset(struct omap_mcspi_s * s)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 
omap_mcspi_read(void * opaque,target_phys_addr_t addr)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 
omap_mcspi_write(void * opaque,target_phys_addr_t addr,uint32_t value)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 
omap_mcspi_init(struct omap_target_agent_s * ta,int chnum,qemu_irq irq,qemu_irq * drq,omap_clk fclk,omap_clk iclk)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 
omap_mcspi_attach(struct omap_mcspi_s * s,uint32_t (* txrx)(void * opaque,uint32_t,int),void * opaque,int chipselect)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 }
347