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