xref: /openbmc/qemu/hw/dma/i8257.c (revision 28ae3179fc52d2e4d870b635c4a412aab99759e7)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU DMA emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2003-2004 Vassili Karpov (malc)
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
749ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
849ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
949ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1049ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
1149ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
1249ab747fSPaolo Bonzini  *
1349ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
1449ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1749ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1849ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1949ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2049ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2149ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2249ab747fSPaolo Bonzini  * THE SOFTWARE.
2349ab747fSPaolo Bonzini  */
240b8fa32fSMarkus Armbruster 
25b6a0aa05SPeter Maydell #include "qemu/osdep.h"
2649ab747fSPaolo Bonzini #include "hw/isa/isa.h"
27a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
28d6454270SMarkus Armbruster #include "migration/vmstate.h"
2955f613acSPhilippe Mathieu-Daudé #include "hw/dma/i8257.h"
3096927c74SMarkus Armbruster #include "qapi/error.h"
3149ab747fSPaolo Bonzini #include "qemu/main-loop.h"
320b8fa32fSMarkus Armbruster #include "qemu/module.h"
33c1a00ff8SPhilippe Mathieu-Daudé #include "qemu/log.h"
347dbb4c49SPhilipp Hahn #include "trace.h"
3549ab747fSPaolo Bonzini 
36340e19ebSHervé Poussineau 
3749ab747fSPaolo Bonzini /* #define DEBUG_DMA */
3849ab747fSPaolo Bonzini 
3949ab747fSPaolo Bonzini #define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__)
4049ab747fSPaolo Bonzini #ifdef DEBUG_DMA
4149ab747fSPaolo Bonzini #define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__)
4249ab747fSPaolo Bonzini #define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__)
4349ab747fSPaolo Bonzini #else
4449ab747fSPaolo Bonzini #define linfo(...)
4549ab747fSPaolo Bonzini #define ldebug(...)
4649ab747fSPaolo Bonzini #endif
4749ab747fSPaolo Bonzini 
4849ab747fSPaolo Bonzini #define ADDR 0
4949ab747fSPaolo Bonzini #define COUNT 1
5049ab747fSPaolo Bonzini 
5149ab747fSPaolo Bonzini enum {
5249ab747fSPaolo Bonzini     CMD_MEMORY_TO_MEMORY = 0x01,
5349ab747fSPaolo Bonzini     CMD_FIXED_ADDRESS    = 0x02,
5449ab747fSPaolo Bonzini     CMD_BLOCK_CONTROLLER = 0x04,
5549ab747fSPaolo Bonzini     CMD_COMPRESSED_TIME  = 0x08,
5649ab747fSPaolo Bonzini     CMD_CYCLIC_PRIORITY  = 0x10,
5749ab747fSPaolo Bonzini     CMD_EXTENDED_WRITE   = 0x20,
5849ab747fSPaolo Bonzini     CMD_LOW_DREQ         = 0x40,
5949ab747fSPaolo Bonzini     CMD_LOW_DACK         = 0x80,
6049ab747fSPaolo Bonzini     CMD_NOT_SUPPORTED    = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS
6149ab747fSPaolo Bonzini     | CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE
6249ab747fSPaolo Bonzini     | CMD_LOW_DREQ | CMD_LOW_DACK
6349ab747fSPaolo Bonzini 
6449ab747fSPaolo Bonzini };
6549ab747fSPaolo Bonzini 
66b9ebd28cSHervé Poussineau static void i8257_dma_run(void *opaque);
6749ab747fSPaolo Bonzini 
688d3c4c81SHervé Poussineau static const int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
6949ab747fSPaolo Bonzini 
i8257_write_page(void * opaque,uint32_t nport,uint32_t data)7074c47de0SHervé Poussineau static void i8257_write_page(void *opaque, uint32_t nport, uint32_t data)
7149ab747fSPaolo Bonzini {
726a128b13SHervé Poussineau     I8257State *d = opaque;
7349ab747fSPaolo Bonzini     int ichan;
7449ab747fSPaolo Bonzini 
7549ab747fSPaolo Bonzini     ichan = channels[nport & 7];
7649ab747fSPaolo Bonzini     if (-1 == ichan) {
7749ab747fSPaolo Bonzini         dolog ("invalid channel %#x %#x\n", nport, data);
7849ab747fSPaolo Bonzini         return;
7949ab747fSPaolo Bonzini     }
8049ab747fSPaolo Bonzini     d->regs[ichan].page = data;
8149ab747fSPaolo Bonzini }
8249ab747fSPaolo Bonzini 
i8257_write_pageh(void * opaque,uint32_t nport,uint32_t data)8374c47de0SHervé Poussineau static void i8257_write_pageh(void *opaque, uint32_t nport, uint32_t data)
8449ab747fSPaolo Bonzini {
856a128b13SHervé Poussineau     I8257State *d = opaque;
8649ab747fSPaolo Bonzini     int ichan;
8749ab747fSPaolo Bonzini 
8849ab747fSPaolo Bonzini     ichan = channels[nport & 7];
8949ab747fSPaolo Bonzini     if (-1 == ichan) {
9049ab747fSPaolo Bonzini         dolog ("invalid channel %#x %#x\n", nport, data);
9149ab747fSPaolo Bonzini         return;
9249ab747fSPaolo Bonzini     }
9349ab747fSPaolo Bonzini     d->regs[ichan].pageh = data;
9449ab747fSPaolo Bonzini }
9549ab747fSPaolo Bonzini 
i8257_read_page(void * opaque,uint32_t nport)9674c47de0SHervé Poussineau static uint32_t i8257_read_page(void *opaque, uint32_t nport)
9749ab747fSPaolo Bonzini {
986a128b13SHervé Poussineau     I8257State *d = opaque;
9949ab747fSPaolo Bonzini     int ichan;
10049ab747fSPaolo Bonzini 
10149ab747fSPaolo Bonzini     ichan = channels[nport & 7];
10249ab747fSPaolo Bonzini     if (-1 == ichan) {
10349ab747fSPaolo Bonzini         dolog ("invalid channel read %#x\n", nport);
10449ab747fSPaolo Bonzini         return 0;
10549ab747fSPaolo Bonzini     }
10649ab747fSPaolo Bonzini     return d->regs[ichan].page;
10749ab747fSPaolo Bonzini }
10849ab747fSPaolo Bonzini 
i8257_read_pageh(void * opaque,uint32_t nport)10974c47de0SHervé Poussineau static uint32_t i8257_read_pageh(void *opaque, uint32_t nport)
11049ab747fSPaolo Bonzini {
1116a128b13SHervé Poussineau     I8257State *d = opaque;
11249ab747fSPaolo Bonzini     int ichan;
11349ab747fSPaolo Bonzini 
11449ab747fSPaolo Bonzini     ichan = channels[nport & 7];
11549ab747fSPaolo Bonzini     if (-1 == ichan) {
11649ab747fSPaolo Bonzini         dolog ("invalid channel read %#x\n", nport);
11749ab747fSPaolo Bonzini         return 0;
11849ab747fSPaolo Bonzini     }
11949ab747fSPaolo Bonzini     return d->regs[ichan].pageh;
12049ab747fSPaolo Bonzini }
12149ab747fSPaolo Bonzini 
i8257_init_chan(I8257State * d,int ichan)12274c47de0SHervé Poussineau static inline void i8257_init_chan(I8257State *d, int ichan)
12349ab747fSPaolo Bonzini {
1240eee6d62SHervé Poussineau     I8257Regs *r;
12549ab747fSPaolo Bonzini 
12649ab747fSPaolo Bonzini     r = d->regs + ichan;
12749ab747fSPaolo Bonzini     r->now[ADDR] = r->base[ADDR] << d->dshift;
12849ab747fSPaolo Bonzini     r->now[COUNT] = 0;
12949ab747fSPaolo Bonzini }
13049ab747fSPaolo Bonzini 
i8257_getff(I8257State * d)13174c47de0SHervé Poussineau static inline int i8257_getff(I8257State *d)
13249ab747fSPaolo Bonzini {
13349ab747fSPaolo Bonzini     int ff;
13449ab747fSPaolo Bonzini 
13549ab747fSPaolo Bonzini     ff = d->flip_flop;
13649ab747fSPaolo Bonzini     d->flip_flop = !ff;
13749ab747fSPaolo Bonzini     return ff;
13849ab747fSPaolo Bonzini }
13949ab747fSPaolo Bonzini 
i8257_read_chan(void * opaque,hwaddr nport,unsigned size)14074c47de0SHervé Poussineau static uint64_t i8257_read_chan(void *opaque, hwaddr nport, unsigned size)
14149ab747fSPaolo Bonzini {
1426a128b13SHervé Poussineau     I8257State *d = opaque;
14349ab747fSPaolo Bonzini     int ichan, nreg, iport, ff, val, dir;
1440eee6d62SHervé Poussineau     I8257Regs *r;
14549ab747fSPaolo Bonzini 
14649ab747fSPaolo Bonzini     iport = (nport >> d->dshift) & 0x0f;
14749ab747fSPaolo Bonzini     ichan = iport >> 1;
14849ab747fSPaolo Bonzini     nreg = iport & 1;
14949ab747fSPaolo Bonzini     r = d->regs + ichan;
15049ab747fSPaolo Bonzini 
15149ab747fSPaolo Bonzini     dir = ((r->mode >> 5) & 1) ? -1 : 1;
15274c47de0SHervé Poussineau     ff = i8257_getff(d);
15349ab747fSPaolo Bonzini     if (nreg)
15449ab747fSPaolo Bonzini         val = (r->base[COUNT] << d->dshift) - r->now[COUNT];
15549ab747fSPaolo Bonzini     else
15649ab747fSPaolo Bonzini         val = r->now[ADDR] + r->now[COUNT] * dir;
15749ab747fSPaolo Bonzini 
15849ab747fSPaolo Bonzini     ldebug ("read_chan %#x -> %d\n", iport, val);
15949ab747fSPaolo Bonzini     return (val >> (d->dshift + (ff << 3))) & 0xff;
16049ab747fSPaolo Bonzini }
16149ab747fSPaolo Bonzini 
i8257_write_chan(void * opaque,hwaddr nport,uint64_t data,unsigned int size)16274c47de0SHervé Poussineau static void i8257_write_chan(void *opaque, hwaddr nport, uint64_t data,
16374c47de0SHervé Poussineau                              unsigned int size)
16449ab747fSPaolo Bonzini {
1656a128b13SHervé Poussineau     I8257State *d = opaque;
16649ab747fSPaolo Bonzini     int iport, ichan, nreg;
1670eee6d62SHervé Poussineau     I8257Regs *r;
16849ab747fSPaolo Bonzini 
16949ab747fSPaolo Bonzini     iport = (nport >> d->dshift) & 0x0f;
17049ab747fSPaolo Bonzini     ichan = iport >> 1;
17149ab747fSPaolo Bonzini     nreg = iport & 1;
17249ab747fSPaolo Bonzini     r = d->regs + ichan;
17374c47de0SHervé Poussineau     if (i8257_getff(d)) {
17449ab747fSPaolo Bonzini         r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00);
17574c47de0SHervé Poussineau         i8257_init_chan(d, ichan);
17649ab747fSPaolo Bonzini     } else {
17749ab747fSPaolo Bonzini         r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff);
17849ab747fSPaolo Bonzini     }
17949ab747fSPaolo Bonzini }
18049ab747fSPaolo Bonzini 
i8257_write_cont(void * opaque,hwaddr nport,uint64_t data,unsigned int size)18174c47de0SHervé Poussineau static void i8257_write_cont(void *opaque, hwaddr nport, uint64_t data,
18274c47de0SHervé Poussineau                              unsigned int size)
18349ab747fSPaolo Bonzini {
1846a128b13SHervé Poussineau     I8257State *d = opaque;
18549ab747fSPaolo Bonzini     int iport, ichan = 0;
18649ab747fSPaolo Bonzini 
18749ab747fSPaolo Bonzini     iport = (nport >> d->dshift) & 0x0f;
18849ab747fSPaolo Bonzini     switch (iport) {
18949ab747fSPaolo Bonzini     case 0x00:                  /* command */
19049ab747fSPaolo Bonzini         if ((data != 0) && (data & CMD_NOT_SUPPORTED)) {
191c1a00ff8SPhilippe Mathieu-Daudé             qemu_log_mask(LOG_UNIMP, "%s: cmd 0x%02"PRIx64" not supported\n",
192c1a00ff8SPhilippe Mathieu-Daudé                           __func__, data);
19349ab747fSPaolo Bonzini             return;
19449ab747fSPaolo Bonzini         }
19549ab747fSPaolo Bonzini         d->command = data;
19649ab747fSPaolo Bonzini         break;
19749ab747fSPaolo Bonzini 
19849ab747fSPaolo Bonzini     case 0x01:
19949ab747fSPaolo Bonzini         ichan = data & 3;
20049ab747fSPaolo Bonzini         if (data & 4) {
20149ab747fSPaolo Bonzini             d->status |= 1 << (ichan + 4);
20249ab747fSPaolo Bonzini         }
20349ab747fSPaolo Bonzini         else {
20449ab747fSPaolo Bonzini             d->status &= ~(1 << (ichan + 4));
20549ab747fSPaolo Bonzini         }
20649ab747fSPaolo Bonzini         d->status &= ~(1 << ichan);
207b9ebd28cSHervé Poussineau         i8257_dma_run(d);
20849ab747fSPaolo Bonzini         break;
20949ab747fSPaolo Bonzini 
21049ab747fSPaolo Bonzini     case 0x02:                  /* single mask */
21149ab747fSPaolo Bonzini         if (data & 4)
21249ab747fSPaolo Bonzini             d->mask |= 1 << (data & 3);
21349ab747fSPaolo Bonzini         else
21449ab747fSPaolo Bonzini             d->mask &= ~(1 << (data & 3));
215b9ebd28cSHervé Poussineau         i8257_dma_run(d);
21649ab747fSPaolo Bonzini         break;
21749ab747fSPaolo Bonzini 
21849ab747fSPaolo Bonzini     case 0x03:                  /* mode */
21949ab747fSPaolo Bonzini         {
22049ab747fSPaolo Bonzini             ichan = data & 3;
22149ab747fSPaolo Bonzini #ifdef DEBUG_DMA
22249ab747fSPaolo Bonzini             {
22349ab747fSPaolo Bonzini                 int op, ai, dir, opmode;
22449ab747fSPaolo Bonzini                 op = (data >> 2) & 3;
22549ab747fSPaolo Bonzini                 ai = (data >> 4) & 1;
22649ab747fSPaolo Bonzini                 dir = (data >> 5) & 1;
22749ab747fSPaolo Bonzini                 opmode = (data >> 6) & 3;
22849ab747fSPaolo Bonzini 
22949ab747fSPaolo Bonzini                 linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n",
23049ab747fSPaolo Bonzini                        ichan, op, ai, dir, opmode);
23149ab747fSPaolo Bonzini             }
23249ab747fSPaolo Bonzini #endif
23349ab747fSPaolo Bonzini             d->regs[ichan].mode = data;
23449ab747fSPaolo Bonzini             break;
23549ab747fSPaolo Bonzini         }
23649ab747fSPaolo Bonzini 
23749ab747fSPaolo Bonzini     case 0x04:                  /* clear flip flop */
23849ab747fSPaolo Bonzini         d->flip_flop = 0;
23949ab747fSPaolo Bonzini         break;
24049ab747fSPaolo Bonzini 
24149ab747fSPaolo Bonzini     case 0x05:                  /* reset */
24249ab747fSPaolo Bonzini         d->flip_flop = 0;
24349ab747fSPaolo Bonzini         d->mask = ~0;
24449ab747fSPaolo Bonzini         d->status = 0;
24549ab747fSPaolo Bonzini         d->command = 0;
24649ab747fSPaolo Bonzini         break;
24749ab747fSPaolo Bonzini 
24849ab747fSPaolo Bonzini     case 0x06:                  /* clear mask for all channels */
24949ab747fSPaolo Bonzini         d->mask = 0;
250b9ebd28cSHervé Poussineau         i8257_dma_run(d);
25149ab747fSPaolo Bonzini         break;
25249ab747fSPaolo Bonzini 
25349ab747fSPaolo Bonzini     case 0x07:                  /* write mask for all channels */
25449ab747fSPaolo Bonzini         d->mask = data;
255b9ebd28cSHervé Poussineau         i8257_dma_run(d);
25649ab747fSPaolo Bonzini         break;
25749ab747fSPaolo Bonzini 
25849ab747fSPaolo Bonzini     default:
25949ab747fSPaolo Bonzini         dolog ("unknown iport %#x\n", iport);
26049ab747fSPaolo Bonzini         break;
26149ab747fSPaolo Bonzini     }
26249ab747fSPaolo Bonzini 
26349ab747fSPaolo Bonzini #ifdef DEBUG_DMA
26449ab747fSPaolo Bonzini     if (0xc != iport) {
26549ab747fSPaolo Bonzini         linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n",
26649ab747fSPaolo Bonzini                nport, ichan, data);
26749ab747fSPaolo Bonzini     }
26849ab747fSPaolo Bonzini #endif
26949ab747fSPaolo Bonzini }
27049ab747fSPaolo Bonzini 
i8257_read_cont(void * opaque,hwaddr nport,unsigned size)27174c47de0SHervé Poussineau static uint64_t i8257_read_cont(void *opaque, hwaddr nport, unsigned size)
27249ab747fSPaolo Bonzini {
2736a128b13SHervé Poussineau     I8257State *d = opaque;
27449ab747fSPaolo Bonzini     int iport, val;
27549ab747fSPaolo Bonzini 
27649ab747fSPaolo Bonzini     iport = (nport >> d->dshift) & 0x0f;
27749ab747fSPaolo Bonzini     switch (iport) {
27849ab747fSPaolo Bonzini     case 0x00:                  /* status */
27949ab747fSPaolo Bonzini         val = d->status;
28049ab747fSPaolo Bonzini         d->status &= 0xf0;
28149ab747fSPaolo Bonzini         break;
28249ab747fSPaolo Bonzini     case 0x01:                  /* mask */
28349ab747fSPaolo Bonzini         val = d->mask;
28449ab747fSPaolo Bonzini         break;
28549ab747fSPaolo Bonzini     default:
28649ab747fSPaolo Bonzini         val = 0;
28749ab747fSPaolo Bonzini         break;
28849ab747fSPaolo Bonzini     }
28949ab747fSPaolo Bonzini 
29049ab747fSPaolo Bonzini     ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val);
29149ab747fSPaolo Bonzini     return val;
29249ab747fSPaolo Bonzini }
29349ab747fSPaolo Bonzini 
i8257_dma_has_autoinitialization(IsaDma * obj,int nchan)29416ffe363SHervé Poussineau static bool i8257_dma_has_autoinitialization(IsaDma *obj, int nchan)
29549ab747fSPaolo Bonzini {
29616ffe363SHervé Poussineau     I8257State *d = I8257(obj);
29716ffe363SHervé Poussineau     return (d->regs[nchan & 3].mode >> 4) & 1;
29849ab747fSPaolo Bonzini }
29949ab747fSPaolo Bonzini 
i8257_dma_hold_DREQ(IsaDma * obj,int nchan)30016ffe363SHervé Poussineau static void i8257_dma_hold_DREQ(IsaDma *obj, int nchan)
30149ab747fSPaolo Bonzini {
30216ffe363SHervé Poussineau     I8257State *d = I8257(obj);
30316ffe363SHervé Poussineau     int ichan;
30449ab747fSPaolo Bonzini 
30549ab747fSPaolo Bonzini     ichan = nchan & 3;
30616ffe363SHervé Poussineau     d->status |= 1 << (ichan + 4);
30716ffe363SHervé Poussineau     i8257_dma_run(d);
30816ffe363SHervé Poussineau }
30916ffe363SHervé Poussineau 
i8257_dma_release_DREQ(IsaDma * obj,int nchan)31016ffe363SHervé Poussineau static void i8257_dma_release_DREQ(IsaDma *obj, int nchan)
31116ffe363SHervé Poussineau {
31216ffe363SHervé Poussineau     I8257State *d = I8257(obj);
31316ffe363SHervé Poussineau     int ichan;
31416ffe363SHervé Poussineau 
31516ffe363SHervé Poussineau     ichan = nchan & 3;
31616ffe363SHervé Poussineau     d->status &= ~(1 << (ichan + 4));
31716ffe363SHervé Poussineau     i8257_dma_run(d);
31849ab747fSPaolo Bonzini }
31949ab747fSPaolo Bonzini 
i8257_channel_run(I8257State * d,int ichan)320b9ebd28cSHervé Poussineau static void i8257_channel_run(I8257State *d, int ichan)
32149ab747fSPaolo Bonzini {
322b9ebd28cSHervé Poussineau     int ncont = d->dshift;
32349ab747fSPaolo Bonzini     int n;
324b9ebd28cSHervé Poussineau     I8257Regs *r = &d->regs[ichan];
32549ab747fSPaolo Bonzini #ifdef DEBUG_DMA
32649ab747fSPaolo Bonzini     int dir, opmode;
32749ab747fSPaolo Bonzini 
32849ab747fSPaolo Bonzini     dir = (r->mode >> 5) & 1;
32949ab747fSPaolo Bonzini     opmode = (r->mode >> 6) & 3;
33049ab747fSPaolo Bonzini 
33149ab747fSPaolo Bonzini     if (dir) {
33249ab747fSPaolo Bonzini         dolog ("DMA in address decrement mode\n");
33349ab747fSPaolo Bonzini     }
33449ab747fSPaolo Bonzini     if (opmode != 1) {
33549ab747fSPaolo Bonzini         dolog ("DMA not in single mode select %#x\n", opmode);
33649ab747fSPaolo Bonzini     }
33749ab747fSPaolo Bonzini #endif
33849ab747fSPaolo Bonzini 
33949ab747fSPaolo Bonzini     n = r->transfer_handler (r->opaque, ichan + (ncont << 2),
34049ab747fSPaolo Bonzini                              r->now[COUNT], (r->base[COUNT] + 1) << ncont);
34149ab747fSPaolo Bonzini     r->now[COUNT] = n;
34249ab747fSPaolo Bonzini     ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
343bb8f32c0SHervé Poussineau     if (n == (r->base[COUNT] + 1) << ncont) {
344bb8f32c0SHervé Poussineau         ldebug("transfer done\n");
345bb8f32c0SHervé Poussineau         d->status |= (1 << ichan);
346bb8f32c0SHervé Poussineau     }
34749ab747fSPaolo Bonzini }
34849ab747fSPaolo Bonzini 
i8257_dma_run(void * opaque)349b9ebd28cSHervé Poussineau static void i8257_dma_run(void *opaque)
35049ab747fSPaolo Bonzini {
351b9ebd28cSHervé Poussineau     I8257State *d = opaque;
352b9ebd28cSHervé Poussineau     int ichan;
35349ab747fSPaolo Bonzini     int rearm = 0;
35449ab747fSPaolo Bonzini 
355b9ebd28cSHervé Poussineau     if (d->running) {
35649ab747fSPaolo Bonzini         rearm = 1;
35749ab747fSPaolo Bonzini         goto out;
35849ab747fSPaolo Bonzini     } else {
359b9ebd28cSHervé Poussineau         d->running = 1;
36049ab747fSPaolo Bonzini     }
36149ab747fSPaolo Bonzini 
36249ab747fSPaolo Bonzini     for (ichan = 0; ichan < 4; ichan++) {
36349ab747fSPaolo Bonzini         int mask;
36449ab747fSPaolo Bonzini 
36549ab747fSPaolo Bonzini         mask = 1 << ichan;
36649ab747fSPaolo Bonzini 
36749ab747fSPaolo Bonzini         if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4)))) {
368b9ebd28cSHervé Poussineau             i8257_channel_run(d, ichan);
36949ab747fSPaolo Bonzini             rearm = 1;
37049ab747fSPaolo Bonzini         }
37149ab747fSPaolo Bonzini     }
37249ab747fSPaolo Bonzini 
373b9ebd28cSHervé Poussineau     d->running = 0;
37449ab747fSPaolo Bonzini out:
37519d2b5e6SPaolo Bonzini     if (rearm) {
376b9ebd28cSHervé Poussineau         qemu_bh_schedule_idle(d->dma_bh);
377b9ebd28cSHervé Poussineau         d->dma_bh_scheduled = true;
37819d2b5e6SPaolo Bonzini     }
37949ab747fSPaolo Bonzini }
38049ab747fSPaolo Bonzini 
i8257_dma_register_channel(IsaDma * obj,int nchan,IsaDmaTransferHandler transfer_handler,void * opaque)38116ffe363SHervé Poussineau static void i8257_dma_register_channel(IsaDma *obj, int nchan,
382bd36a618SMarkus Armbruster                                        IsaDmaTransferHandler transfer_handler,
38349ab747fSPaolo Bonzini                                        void *opaque)
38449ab747fSPaolo Bonzini {
38516ffe363SHervé Poussineau     I8257State *d = I8257(obj);
3860eee6d62SHervé Poussineau     I8257Regs *r;
38716ffe363SHervé Poussineau     int ichan;
38849ab747fSPaolo Bonzini 
38949ab747fSPaolo Bonzini     ichan = nchan & 3;
39049ab747fSPaolo Bonzini 
39116ffe363SHervé Poussineau     r = d->regs + ichan;
39249ab747fSPaolo Bonzini     r->transfer_handler = transfer_handler;
39349ab747fSPaolo Bonzini     r->opaque = opaque;
39449ab747fSPaolo Bonzini }
39549ab747fSPaolo Bonzini 
i8257_is_verify_transfer(I8257Regs * r)3969e58f172SSven Schnelle static bool i8257_is_verify_transfer(I8257Regs *r)
3979e58f172SSven Schnelle {
3989e58f172SSven Schnelle     return (r->mode & 0x0c) == 0;
3999e58f172SSven Schnelle }
4009e58f172SSven Schnelle 
i8257_dma_read_memory(IsaDma * obj,int nchan,void * buf,int pos,int len)40116ffe363SHervé Poussineau static int i8257_dma_read_memory(IsaDma *obj, int nchan, void *buf, int pos,
40216ffe363SHervé Poussineau                                  int len)
40349ab747fSPaolo Bonzini {
40416ffe363SHervé Poussineau     I8257State *d = I8257(obj);
40516ffe363SHervé Poussineau     I8257Regs *r = &d->regs[nchan & 3];
40649ab747fSPaolo Bonzini     hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
40749ab747fSPaolo Bonzini 
4089e58f172SSven Schnelle     if (i8257_is_verify_transfer(r)) {
4099e58f172SSven Schnelle         return len;
4109e58f172SSven Schnelle     }
4119e58f172SSven Schnelle 
41249ab747fSPaolo Bonzini     if (r->mode & 0x20) {
41349ab747fSPaolo Bonzini         int i;
41449ab747fSPaolo Bonzini         uint8_t *p = buf;
41549ab747fSPaolo Bonzini 
41649ab747fSPaolo Bonzini         cpu_physical_memory_read (addr - pos - len, buf, len);
41749ab747fSPaolo Bonzini         /* What about 16bit transfers? */
41849ab747fSPaolo Bonzini         for (i = 0; i < len >> 1; i++) {
41949ab747fSPaolo Bonzini             uint8_t b = p[len - i - 1];
42049ab747fSPaolo Bonzini             p[i] = b;
42149ab747fSPaolo Bonzini         }
42249ab747fSPaolo Bonzini     }
42349ab747fSPaolo Bonzini     else
42449ab747fSPaolo Bonzini         cpu_physical_memory_read (addr + pos, buf, len);
42549ab747fSPaolo Bonzini 
42649ab747fSPaolo Bonzini     return len;
42749ab747fSPaolo Bonzini }
42849ab747fSPaolo Bonzini 
i8257_dma_write_memory(IsaDma * obj,int nchan,void * buf,int pos,int len)42916ffe363SHervé Poussineau static int i8257_dma_write_memory(IsaDma *obj, int nchan, void *buf, int pos,
43016ffe363SHervé Poussineau                                  int len)
43149ab747fSPaolo Bonzini {
43216ffe363SHervé Poussineau     I8257State *s = I8257(obj);
43316ffe363SHervé Poussineau     I8257Regs *r = &s->regs[nchan & 3];
43449ab747fSPaolo Bonzini     hwaddr addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
43549ab747fSPaolo Bonzini 
4369e58f172SSven Schnelle     if (i8257_is_verify_transfer(r)) {
4379e58f172SSven Schnelle         return len;
4389e58f172SSven Schnelle     }
4399e58f172SSven Schnelle 
44049ab747fSPaolo Bonzini     if (r->mode & 0x20) {
44149ab747fSPaolo Bonzini         int i;
44249ab747fSPaolo Bonzini         uint8_t *p = buf;
44349ab747fSPaolo Bonzini 
44449ab747fSPaolo Bonzini         cpu_physical_memory_write (addr - pos - len, buf, len);
44549ab747fSPaolo Bonzini         /* What about 16bit transfers? */
44649ab747fSPaolo Bonzini         for (i = 0; i < len; i++) {
44749ab747fSPaolo Bonzini             uint8_t b = p[len - i - 1];
44849ab747fSPaolo Bonzini             p[i] = b;
44949ab747fSPaolo Bonzini         }
45049ab747fSPaolo Bonzini     }
45149ab747fSPaolo Bonzini     else
45249ab747fSPaolo Bonzini         cpu_physical_memory_write (addr + pos, buf, len);
45349ab747fSPaolo Bonzini 
45449ab747fSPaolo Bonzini     return len;
45549ab747fSPaolo Bonzini }
45649ab747fSPaolo Bonzini 
45719d2b5e6SPaolo Bonzini /* request the emulator to transfer a new DMA memory block ASAP (even
45819d2b5e6SPaolo Bonzini  * if the idle bottom half would not have exited the iothread yet).
45919d2b5e6SPaolo Bonzini  */
i8257_dma_schedule(IsaDma * obj)46016ffe363SHervé Poussineau static void i8257_dma_schedule(IsaDma *obj)
46149ab747fSPaolo Bonzini {
46216ffe363SHervé Poussineau     I8257State *d = I8257(obj);
46316ffe363SHervé Poussineau     if (d->dma_bh_scheduled) {
46419d2b5e6SPaolo Bonzini         qemu_notify_event();
46519d2b5e6SPaolo Bonzini     }
46649ab747fSPaolo Bonzini }
46749ab747fSPaolo Bonzini 
i8257_reset(DeviceState * dev)468340e19ebSHervé Poussineau static void i8257_reset(DeviceState *dev)
46949ab747fSPaolo Bonzini {
470340e19ebSHervé Poussineau     I8257State *d = I8257(dev);
47174c47de0SHervé Poussineau     i8257_write_cont(d, (0x05 << d->dshift), 0, 1);
47249ab747fSPaolo Bonzini }
47349ab747fSPaolo Bonzini 
i8257_phony_handler(void * opaque,int nchan,int dma_pos,int dma_len)47474c47de0SHervé Poussineau static int i8257_phony_handler(void *opaque, int nchan, int dma_pos,
47574c47de0SHervé Poussineau                                int dma_len)
47649ab747fSPaolo Bonzini {
4777dbb4c49SPhilipp Hahn     trace_i8257_unregistered_dma(nchan, dma_pos, dma_len);
47849ab747fSPaolo Bonzini     return dma_pos;
47949ab747fSPaolo Bonzini }
48049ab747fSPaolo Bonzini 
48149ab747fSPaolo Bonzini 
48249ab747fSPaolo Bonzini static const MemoryRegionOps channel_io_ops = {
48374c47de0SHervé Poussineau     .read = i8257_read_chan,
48474c47de0SHervé Poussineau     .write = i8257_write_chan,
48549ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
48649ab747fSPaolo Bonzini     .impl = {
48749ab747fSPaolo Bonzini         .min_access_size = 1,
48849ab747fSPaolo Bonzini         .max_access_size = 1,
48949ab747fSPaolo Bonzini     },
49049ab747fSPaolo Bonzini };
49149ab747fSPaolo Bonzini 
49249ab747fSPaolo Bonzini /* IOport from page_base */
49349ab747fSPaolo Bonzini static const MemoryRegionPortio page_portio_list[] = {
49474c47de0SHervé Poussineau     { 0x01, 3, 1, .write = i8257_write_page, .read = i8257_read_page, },
49574c47de0SHervé Poussineau     { 0x07, 1, 1, .write = i8257_write_page, .read = i8257_read_page, },
49649ab747fSPaolo Bonzini     PORTIO_END_OF_LIST(),
49749ab747fSPaolo Bonzini };
49849ab747fSPaolo Bonzini 
49949ab747fSPaolo Bonzini /* IOport from pageh_base */
50049ab747fSPaolo Bonzini static const MemoryRegionPortio pageh_portio_list[] = {
50174c47de0SHervé Poussineau     { 0x01, 3, 1, .write = i8257_write_pageh, .read = i8257_read_pageh, },
50274c47de0SHervé Poussineau     { 0x07, 3, 1, .write = i8257_write_pageh, .read = i8257_read_pageh, },
50349ab747fSPaolo Bonzini     PORTIO_END_OF_LIST(),
50449ab747fSPaolo Bonzini };
50549ab747fSPaolo Bonzini 
50649ab747fSPaolo Bonzini static const MemoryRegionOps cont_io_ops = {
50774c47de0SHervé Poussineau     .read = i8257_read_cont,
50874c47de0SHervé Poussineau     .write = i8257_write_cont,
50949ab747fSPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
51049ab747fSPaolo Bonzini     .impl = {
51149ab747fSPaolo Bonzini         .min_access_size = 1,
51249ab747fSPaolo Bonzini         .max_access_size = 1,
51349ab747fSPaolo Bonzini     },
51449ab747fSPaolo Bonzini };
51549ab747fSPaolo Bonzini 
5160eee6d62SHervé Poussineau static const VMStateDescription vmstate_i8257_regs = {
51749ab747fSPaolo Bonzini     .name = "dma_regs",
51849ab747fSPaolo Bonzini     .version_id = 1,
51949ab747fSPaolo Bonzini     .minimum_version_id = 1,
52063e6b564SRichard Henderson     .fields = (const VMStateField[]) {
5210eee6d62SHervé Poussineau         VMSTATE_INT32_ARRAY(now, I8257Regs, 2),
5220eee6d62SHervé Poussineau         VMSTATE_UINT16_ARRAY(base, I8257Regs, 2),
5230eee6d62SHervé Poussineau         VMSTATE_UINT8(mode, I8257Regs),
5240eee6d62SHervé Poussineau         VMSTATE_UINT8(page, I8257Regs),
5250eee6d62SHervé Poussineau         VMSTATE_UINT8(pageh, I8257Regs),
5260eee6d62SHervé Poussineau         VMSTATE_UINT8(dack, I8257Regs),
5270eee6d62SHervé Poussineau         VMSTATE_UINT8(eop, I8257Regs),
52849ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
52949ab747fSPaolo Bonzini     }
53049ab747fSPaolo Bonzini };
53149ab747fSPaolo Bonzini 
i8257_post_load(void * opaque,int version_id)53274c47de0SHervé Poussineau static int i8257_post_load(void *opaque, int version_id)
53349ab747fSPaolo Bonzini {
534b9ebd28cSHervé Poussineau     I8257State *d = opaque;
535b9ebd28cSHervé Poussineau     i8257_dma_run(d);
53649ab747fSPaolo Bonzini 
53749ab747fSPaolo Bonzini     return 0;
53849ab747fSPaolo Bonzini }
53949ab747fSPaolo Bonzini 
540340e19ebSHervé Poussineau static const VMStateDescription vmstate_i8257 = {
54149ab747fSPaolo Bonzini     .name = "dma",
54249ab747fSPaolo Bonzini     .version_id = 1,
54349ab747fSPaolo Bonzini     .minimum_version_id = 1,
54474c47de0SHervé Poussineau     .post_load = i8257_post_load,
54563e6b564SRichard Henderson     .fields = (const VMStateField[]) {
5466a128b13SHervé Poussineau         VMSTATE_UINT8(command, I8257State),
5476a128b13SHervé Poussineau         VMSTATE_UINT8(mask, I8257State),
5486a128b13SHervé Poussineau         VMSTATE_UINT8(flip_flop, I8257State),
5496a128b13SHervé Poussineau         VMSTATE_INT32(dshift, I8257State),
5500eee6d62SHervé Poussineau         VMSTATE_STRUCT_ARRAY(regs, I8257State, 4, 1, vmstate_i8257_regs,
5510eee6d62SHervé Poussineau                              I8257Regs),
55249ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
55349ab747fSPaolo Bonzini     }
55449ab747fSPaolo Bonzini };
55549ab747fSPaolo Bonzini 
i8257_realize(DeviceState * dev,Error ** errp)556340e19ebSHervé Poussineau static void i8257_realize(DeviceState *dev, Error **errp)
557340e19ebSHervé Poussineau {
558340e19ebSHervé Poussineau     ISADevice *isa = ISA_DEVICE(dev);
559340e19ebSHervé Poussineau     I8257State *d = I8257(dev);
560340e19ebSHervé Poussineau     int i;
561340e19ebSHervé Poussineau 
562a8457764SPhilippe Mathieu-Daudé     memory_region_init_io(&d->channel_io, OBJECT(dev), &channel_io_ops, d,
563340e19ebSHervé Poussineau                           "dma-chan", 8 << d->dshift);
564340e19ebSHervé Poussineau     memory_region_add_subregion(isa_address_space_io(isa),
565340e19ebSHervé Poussineau                                 d->base, &d->channel_io);
566340e19ebSHervé Poussineau 
567e305a165SMarc-André Lureau     isa_register_portio_list(isa, &d->portio_page,
568e305a165SMarc-André Lureau                              d->page_base, page_portio_list, d,
569340e19ebSHervé Poussineau                              "dma-page");
570340e19ebSHervé Poussineau     if (d->pageh_base >= 0) {
571e305a165SMarc-André Lureau         isa_register_portio_list(isa, &d->portio_pageh,
572e305a165SMarc-André Lureau                                  d->pageh_base, pageh_portio_list, d,
573340e19ebSHervé Poussineau                                  "dma-pageh");
574340e19ebSHervé Poussineau     }
575340e19ebSHervé Poussineau 
576340e19ebSHervé Poussineau     memory_region_init_io(&d->cont_io, OBJECT(isa), &cont_io_ops, d,
577340e19ebSHervé Poussineau                           "dma-cont", 8 << d->dshift);
578340e19ebSHervé Poussineau     memory_region_add_subregion(isa_address_space_io(isa),
579340e19ebSHervé Poussineau                                 d->base + (8 << d->dshift), &d->cont_io);
580340e19ebSHervé Poussineau 
581340e19ebSHervé Poussineau     for (i = 0; i < ARRAY_SIZE(d->regs); ++i) {
582340e19ebSHervé Poussineau         d->regs[i].transfer_handler = i8257_phony_handler;
583340e19ebSHervé Poussineau     }
584340e19ebSHervé Poussineau 
585340e19ebSHervé Poussineau     d->dma_bh = qemu_bh_new(i8257_dma_run, d);
586340e19ebSHervé Poussineau }
587340e19ebSHervé Poussineau 
588340e19ebSHervé Poussineau static Property i8257_properties[] = {
589340e19ebSHervé Poussineau     DEFINE_PROP_INT32("base", I8257State, base, 0x00),
590340e19ebSHervé Poussineau     DEFINE_PROP_INT32("page-base", I8257State, page_base, 0x80),
591340e19ebSHervé Poussineau     DEFINE_PROP_INT32("pageh-base", I8257State, pageh_base, 0x480),
592340e19ebSHervé Poussineau     DEFINE_PROP_INT32("dshift", I8257State, dshift, 0),
593340e19ebSHervé Poussineau     DEFINE_PROP_END_OF_LIST()
594340e19ebSHervé Poussineau };
595340e19ebSHervé Poussineau 
i8257_class_init(ObjectClass * klass,void * data)596340e19ebSHervé Poussineau static void i8257_class_init(ObjectClass *klass, void *data)
597340e19ebSHervé Poussineau {
598340e19ebSHervé Poussineau     DeviceClass *dc = DEVICE_CLASS(klass);
59916ffe363SHervé Poussineau     IsaDmaClass *idc = ISADMA_CLASS(klass);
600340e19ebSHervé Poussineau 
601340e19ebSHervé Poussineau     dc->realize = i8257_realize;
602*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, i8257_reset);
603340e19ebSHervé Poussineau     dc->vmsd = &vmstate_i8257;
6044f67d30bSMarc-André Lureau     device_class_set_props(dc, i8257_properties);
60516ffe363SHervé Poussineau 
60616ffe363SHervé Poussineau     idc->has_autoinitialization = i8257_dma_has_autoinitialization;
60716ffe363SHervé Poussineau     idc->read_memory = i8257_dma_read_memory;
60816ffe363SHervé Poussineau     idc->write_memory = i8257_dma_write_memory;
60916ffe363SHervé Poussineau     idc->hold_DREQ = i8257_dma_hold_DREQ;
61016ffe363SHervé Poussineau     idc->release_DREQ = i8257_dma_release_DREQ;
61116ffe363SHervé Poussineau     idc->schedule = i8257_dma_schedule;
61216ffe363SHervé Poussineau     idc->register_channel = i8257_dma_register_channel;
613a952c186SMarkus Armbruster     /* Reason: needs to be wired up by isa_bus_dma() to work */
614e90f2a8cSEduardo Habkost     dc->user_creatable = false;
61516ffe363SHervé Poussineau }
61616ffe363SHervé Poussineau 
617340e19ebSHervé Poussineau static const TypeInfo i8257_info = {
618340e19ebSHervé Poussineau     .name = TYPE_I8257,
619340e19ebSHervé Poussineau     .parent = TYPE_ISA_DEVICE,
620340e19ebSHervé Poussineau     .instance_size = sizeof(I8257State),
621340e19ebSHervé Poussineau     .class_init = i8257_class_init,
62216ffe363SHervé Poussineau     .interfaces = (InterfaceInfo[]) {
62316ffe363SHervé Poussineau         { TYPE_ISADMA },
62416ffe363SHervé Poussineau         { }
62516ffe363SHervé Poussineau     }
626340e19ebSHervé Poussineau };
627340e19ebSHervé Poussineau 
i8257_register_types(void)628340e19ebSHervé Poussineau static void i8257_register_types(void)
629340e19ebSHervé Poussineau {
630340e19ebSHervé Poussineau     type_register_static(&i8257_info);
631340e19ebSHervé Poussineau }
632340e19ebSHervé Poussineau 
type_init(i8257_register_types)633340e19ebSHervé Poussineau type_init(i8257_register_types)
634340e19ebSHervé Poussineau 
6355e37bc49SPhilippe Mathieu-Daudé void i8257_dma_init(Object *parent, ISABus *bus, bool high_page_enable)
63649ab747fSPaolo Bonzini {
637340e19ebSHervé Poussineau     ISADevice *isa1, *isa2;
638340e19ebSHervé Poussineau     DeviceState *d;
639340e19ebSHervé Poussineau 
64096927c74SMarkus Armbruster     isa1 = isa_new(TYPE_I8257);
6415e37bc49SPhilippe Mathieu-Daudé     object_property_add_child(parent, "dma[*]", OBJECT(isa1));
642340e19ebSHervé Poussineau     d = DEVICE(isa1);
643340e19ebSHervé Poussineau     qdev_prop_set_int32(d, "base", 0x00);
644340e19ebSHervé Poussineau     qdev_prop_set_int32(d, "page-base", 0x80);
645340e19ebSHervé Poussineau     qdev_prop_set_int32(d, "pageh-base", high_page_enable ? 0x480 : -1);
646340e19ebSHervé Poussineau     qdev_prop_set_int32(d, "dshift", 0);
64796927c74SMarkus Armbruster     isa_realize_and_unref(isa1, bus, &error_fatal);
648340e19ebSHervé Poussineau 
64996927c74SMarkus Armbruster     isa2 = isa_new(TYPE_I8257);
6505e37bc49SPhilippe Mathieu-Daudé     object_property_add_child(parent, "dma[*]", OBJECT(isa2));
651340e19ebSHervé Poussineau     d = DEVICE(isa2);
652340e19ebSHervé Poussineau     qdev_prop_set_int32(d, "base", 0xc0);
653340e19ebSHervé Poussineau     qdev_prop_set_int32(d, "page-base", 0x88);
654340e19ebSHervé Poussineau     qdev_prop_set_int32(d, "pageh-base", high_page_enable ? 0x488 : -1);
655340e19ebSHervé Poussineau     qdev_prop_set_int32(d, "dshift", 1);
65696927c74SMarkus Armbruster     isa_realize_and_unref(isa2, bus, &error_fatal);
65716ffe363SHervé Poussineau 
65816ffe363SHervé Poussineau     isa_bus_dma(bus, ISADMA(isa1), ISADMA(isa2));
65949ab747fSPaolo Bonzini }
660