qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 02/18] hw: add QEMU model for Faraday APB DMA


From: Blue Swirl
Subject: Re: [Qemu-devel] [PATCH 02/18] hw: add QEMU model for Faraday APB DMA
Date: Sat, 19 Jan 2013 08:51:33 +0000

On Fri, Jan 18, 2013 at 6:28 AM, Dante <address@hidden> wrote:
> Signed-off-by: Kuo-Jung Su <address@hidden>
> ---
>  hw/ftapbbrg020.c |  485 
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ftapbbrg020.h |   43 +++++
>  2 files changed, 528 insertions(+)
>  create mode 100644 hw/ftapbbrg020.c
>  create mode 100644 hw/ftapbbrg020.h
>
> diff --git a/hw/ftapbbrg020.c b/hw/ftapbbrg020.c
> new file mode 100644
> index 0000000..3378312
> --- /dev/null
> +++ b/hw/ftapbbrg020.c
> @@ -0,0 +1,485 @@
> +/*
> + * QEMU model of the FTAPBBRG020 DMA Controller
> + *
> + * Copyright (C) 2012 Faraday Technology
> + * Copyright (C) 2012 Dante Su <address@hidden>
> + *
> + * Note: The FTAPBBRG020 DMA decreasing address mode is not implemented.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a 
> copy
> + * of this software and associated documentation files (the "Software"), to 
> deal
> + * in the Software without restriction, including without limitation the 
> rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
> FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "sysbus.h"
> +#include "sysemu/sysemu.h"
> +#include "sysemu/blockdev.h"
> +
> +#include "ftapbbrg020.h"
> +
> +#ifndef min
> +#define min(a, b)    ((a) < (b) ? (a) : (b))
> +#endif
> +
> +#ifndef max
> +#define max(a, b)    ((a) > (b) ? (a) : (b))
> +#endif
> +
> +typedef struct _ftapbbrg020_state ftapbbrg020_state;

Underscores are not allowed at the start of identifiers, see HACKING.

> +
> +typedef struct _ftapbbrg020_chan {
> +    ftapbbrg020_state *chip;
> +
> +    int id;
> +    int burst;
> +    int src_bw;
> +    int src_stride;
> +    int dst_bw;
> +    int dst_stride;
> +
> +    /* HW register caches */
> +    uint32_t src;
> +    uint32_t dst;
> +    uint32_t len;
> +    uint32_t cmd;
> +} ftapbbrg020_chan;
> +
> +typedef struct _ftapbbrg020_state {
> +    SysBusDevice busdev;
> +    MemoryRegion iomem;
> +    qemu_irq irq;
> +
> +    ftapbbrg020_chan chan[4];
> +    qemu_irq         ack[16];
> +    uint32_t         req;
> +
> +    int busy;    /* Busy Channel ID */
> +    QEMUTimer *qtimer;
> +
> +} ftapbbrg020_state;
> +
> +static inline uint32_t
> +ftapbbrg020_get_isr(ftapbbrg020_state *s)
> +{
> +    int i;
> +    uint32_t isr = 0;
> +    ftapbbrg020_chan *chan;
> +
> +    for (i = 0; i < 4; ++i) {
> +        chan = s->chan + i;
> +        isr |= (chan->cmd & 0x12);
> +    }
> +
> +    return isr;
> +}
> +
> +static inline void
> +ftapbbrg020_update_irq(ftapbbrg020_state *s)
> +{
> +    uint32_t isr = ftapbbrg020_get_isr(s);
> +
> +    if (isr)
> +        qemu_set_irq(s->irq, 1);
> +    else
> +        qemu_set_irq(s->irq, 0);
> +}
> +
> +static void
> +ftapbbrg020_chan_cmd_decode(ftapbbrg020_chan *c)
> +{
> +    uint32_t tmp;
> +
> +    /* 1. decode burst size */
> +    c->burst = (c->cmd & 0x08) ? 4 : 1;
> +
> +    /* 2. decode source/destination width */
> +    tmp = (c->cmd >> 20) & 0x03;
> +    if (tmp > 2)
> +        tmp = 2;
> +    c->src_bw = c->dst_bw = 8 << (2 - tmp);
> +
> +    /* 3. decode source address stride */
> +    switch((c->cmd >> 8) & 0x03) {
> +    case 0:
> +        c->src_stride = 0;
> +        break;
> +    case 1:
> +        c->src_stride = c->src_bw >> 3;
> +        break;
> +    case 2:
> +        c->src_stride = 2 * (c->src_bw >> 3);
> +        break;
> +    case 3:
> +        c->src_stride = 4 * (c->src_bw >> 3);
> +        break;
> +    }
> +
> +    /* 4. decode destination address stride */
> +    switch((c->cmd >> 12) & 0x03) {
> +    case 0:
> +        c->dst_stride = 0;
> +        break;
> +    case 1:
> +        c->dst_stride = c->dst_bw >> 3;
> +        break;
> +    case 2:
> +        c->dst_stride = 2 * (c->dst_bw >> 3);
> +        break;
> +    case 3:
> +        c->dst_stride = 4 * (c->dst_bw >> 3);
> +        break;
> +    }
> +}
> +
> +static void
> +ftapbbrg020_chan_start(ftapbbrg020_chan *c)
> +{
> +    ftapbbrg020_state *s = c->chip;
> +    hwaddr src, dst, src_len, dst_len;
> +    uint8_t *src_map = NULL, *dst_map = NULL;
> +    uint8_t *src_ptr = NULL, *dst_ptr = NULL;

src_* could probably be const.

> +    uint8_t buf[4096];
> +    int src_hs = 0, dst_hs = 0, len = 0;
> +
> +    if (!(c->cmd & 0x01))
> +        return;
> +
> +    s->busy = c->id;
> +
> +    src = c->src;
> +    dst = c->dst;
> +    src_hs = (c->cmd >> 24) & 0xf;
> +    dst_hs = (c->cmd >> 16) & 0xf;
> +    src_len = c->src_stride ? (c->len * (c->src_bw >> 3)) : (c->src_bw >> 3);
> +    dst_len = c->dst_stride ? (c->len * (c->dst_bw >> 3)) : (c->dst_bw >> 3);
> +    if (!cpu_physical_memory_is_io(c->src))
> +        src_map = src_ptr = cpu_physical_memory_map(c->src, &src_len, 0);
> +    if (!cpu_physical_memory_is_io(c->dst))
> +        dst_map = dst_ptr = cpu_physical_memory_map(c->dst, &dst_len, 1);
> +
> +    while (c->len > 0) {
> +
> +        if (src_hs && !(s->req & (1 << src_hs)))
> +            break;
> +
> +        if (dst_hs && !(s->req & (1 << dst_hs)))
> +            break;
> +
> +        len = min(sizeof(buf), c->burst * (c->src_bw >> 3));
> +
> +        /* load data from source into local buffer */
> +        if (src_ptr) {
> +            if (c->src_stride) {
> +                memcpy(buf, src_ptr, len);
> +                src += len;
> +                src_ptr += len;
> +            } else {
> +                int i;
> +                switch(c->src_bw) {
> +                case 8:
> +                    for (i = 0; i < len; i += 1)
> +                        *(uint8_t *)(buf + i) = *(uint8_t *)src_ptr;

How is this and the other cases below different from memcpy()?

> +                    break;
> +                case 16:
> +                    for (i = 0; i < len; i += 2)
> +                        *(uint16_t *)(buf + i) = *(uint16_t *)src_ptr;
> +                    break;
> +                default:
> +                    for (i = 0; i < len; i += 4)
> +                        *(uint32_t *)(buf + i) = *(uint32_t *)src_ptr;
> +                    break;
> +                }
> +            }
> +        } else {
> +            uint32_t rl, stride = c->src_bw >> 3;
> +            for (rl = 0; rl < len; rl += stride, src += c->src_stride)
> +                cpu_physical_memory_read(src, (uint64_t *)(buf + rl), 
> stride);
> +        }
> +
> +        /* DMA Hardware Handshake */
> +        if (src_hs) {
> +            qemu_set_irq(s->ack[src_hs], 1);
> +        }
> +
> +        /* store data into destination from local buffer */
> +        if (dst_ptr) {
> +            if (c->dst_stride) {
> +                memcpy(dst_ptr, buf, len);
> +                dst += len;
> +                dst_ptr += len;
> +            } else {
> +                int i;
> +                switch(c->dst_bw) {
> +                case 8:
> +                    for (i = 0; i < len; i += 1)
> +                        *(uint8_t *)dst_ptr = *(uint8_t *)(buf + i);
> +                    break;
> +                case 16:
> +                    for (i = 0; i < len; i += 2)
> +                        *(uint16_t *)dst_ptr = *(uint16_t *)(buf + i);
> +                    break;
> +                default:
> +                    for (i = 0; i < len; i += 4)
> +                        *(uint32_t *)dst_ptr = *(uint32_t *)(buf + i);
> +                    break;
> +                }
> +            }
> +        } else {
> +            uint32_t wl, stride = c->dst_bw >> 3;
> +            for (wl = 0; wl < len; wl += stride, dst += c->dst_stride)
> +                cpu_physical_memory_write(dst, (uint64_t *)(buf + wl), 
> stride);
> +        }
> +
> +        /* DMA Hardware Handshake */
> +        if (dst_hs) {
> +            qemu_set_irq(s->ack[dst_hs], 1);
> +        }
> +
> +        /* update the channel transfer size */
> +        c->len -= len / (c->src_bw >> 3);
> +
> +        if (c->len == 0) {
> +            /* release the memory mappings */
> +            if (src_map) {
> +                cpu_physical_memory_unmap(src_map, src_len, 0, 
> (hwaddr)(src_ptr - src_map));
> +                src_map = src_ptr = NULL;
> +            }
> +            if (dst_map) {
> +                cpu_physical_memory_unmap(dst_map, dst_len, 1, 
> (hwaddr)(dst_ptr - dst_map));
> +                dst_map = dst_ptr = NULL;
> +            }
> +            /* update the channel transfer status */
> +            if (c->cmd & 0x04) {
> +                c->cmd |= 0x02;
> +                ftapbbrg020_update_irq(s);
> +            }
> +            /* clear start bit */
> +            c->cmd &= 0xfffffffe;
> +        }
> +    }
> +
> +    /* release the memory mappings */
> +    if (src_map)
> +        cpu_physical_memory_unmap(src_map, src_len, 0, (hwaddr)(src_ptr - 
> src_map));
> +    if (dst_map)
> +        cpu_physical_memory_unmap(dst_map, dst_len, 1, (hwaddr)(dst_ptr - 
> dst_map));
> +
> +    /* update src/dst address */
> +    c->src = src;
> +    c->dst = dst;
> +
> +    s->busy = -1;
> +}
> +
> +static void
> +ftapbbrg020_chan_reset(ftapbbrg020_chan *c)
> +{
> +    c->cmd = 0;
> +    c->src = 0;
> +    c->dst = 0;
> +    c->len = 0;
> +}
> +
> +static void
> +ftapbbrg020_timer_tick(void *opaque)
> +{
> +    ftapbbrg020_state *s = (ftapbbrg020_state *)opaque;
> +    ftapbbrg020_chan *c = NULL;
> +    int i, jobs, done;
> +
> +    jobs = 0;
> +    done = 0;
> +    for (i = 0; i < 4; ++i) {
> +        c = s->chan + i;
> +        if (c->cmd & 0x01) {
> +            ++jobs;
> +            ftapbbrg020_chan_start(c);
> +            if (!(c->cmd & 0x01))
> +                ++done;
> +        }
> +    }
> +
> +    /* ToDo: Use mutex to skip this periodic checker */
> +    if (jobs - done > 0) {
> +        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
> +    } else {
> +        qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 
> (get_ticks_per_sec() >> 2));
> +    }
> +}
> +
> +static void
> +ftapbbrg020_handle_req(void *opaque, int line, int level)
> +{
> +    ftapbbrg020_state *s = (ftapbbrg020_state *)opaque;

Useless cast in C.

> +
> +    if (level) {
> +        s->req |= (1 << line);
> +    } else {
> +        s->req &= ~(1 << line);
> +        qemu_set_irq(s->ack[line], 0);
> +    }
> +}
> +
> +static void ftapbbrg020_chip_reset(ftapbbrg020_state *s)
> +{
> +    int i;
> +
> +    for (i = 0; i < 4; ++i) {
> +        ftapbbrg020_chan_reset(s->chan + i);
> +    }
> +
> +    /* We can assume our GPIO have been wired up now */
> +    for (i = 0; i < 16; ++i) {
> +        qemu_set_irq(s->ack[i], 0);
> +    }
> +    s->req = 0;
> +}
> +
> +static uint64_t
> +ftapbbrg020_mem_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> +    ftapbbrg020_state *s = opaque;
> +    ftapbbrg020_chan  *c = NULL;
> +    uint32_t ret = 0;
> +
> +    if (addr >= 0x80 && addr < 0xC0) {
> +        c = s->chan + REG_CHAN_ID(addr);
> +        switch(addr & 0x0f) {
> +        case REG_CHAN_CMD:
> +            return c->cmd;
> +        case REG_CHAN_SRC:
> +            return c->src;
> +        case REG_CHAN_DST:
> +            return c->dst;
> +        case REG_CHAN_CYC:
> +            return c->len;
> +        }
> +    } else {
> +        switch(addr) {
> +        case 0xC8:    /* revision register */

The comment could be replaced by self-documenting enum for 0xC8.

> +            return 0x00010800;
> +        default:
> +            break;
> +        }
> +    }
> +
> +    return ret;
> +}
> +
> +static void
> +ftapbbrg020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int 
> size)
> +{
> +    ftapbbrg020_state *s = opaque;
> +    ftapbbrg020_chan  *c = NULL;
> +
> +    if (addr >= 0x80 && addr < 0xC0) {
> +        c = s->chan + REG_CHAN_ID(addr);
> +        switch(addr & 0x0f) {
> +        case REG_CHAN_CMD:
> +            c->cmd = (uint32_t)val;
> +            ftapbbrg020_update_irq(s);
> +            if (c->cmd & 0x01) {
> +                ftapbbrg020_chan_cmd_decode(c);
> +                /* kick-off DMA engine */
> +                qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
> +            }
> +            break;
> +        case REG_CHAN_SRC:
> +            c->src = (uint32_t)val;
> +            break;
> +        case REG_CHAN_DST:
> +            c->dst = (uint32_t)val;
> +            break;
> +        case REG_CHAN_CYC:
> +            c->len = (uint32_t)val & 0x00ffffff;
> +            break;
> +        }
> +    }
> +}
> +
> +static const MemoryRegionOps ftapbbrg020_mem_ops = {
> +    .read  = ftapbbrg020_mem_read,
> +    .write = ftapbbrg020_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void ftapbbrg020_reset(DeviceState *d)
> +{
> +    ftapbbrg020_state *s = DO_UPCAST(ftapbbrg020_state, busdev.qdev, d);
> +    ftapbbrg020_chip_reset(s);
> +}
> +
> +static int ftapbbrg020_init(SysBusDevice *dev)
> +{
> +    ftapbbrg020_state *s = FROM_SYSBUS(typeof(*s), dev);
> +    int i;
> +
> +    memory_region_init_io(&s->iomem, &ftapbbrg020_mem_ops, s, "ftapbbrg020", 
> 0x1000);
> +    sysbus_init_mmio(dev, &s->iomem);
> +    sysbus_init_irq(dev, &s->irq);
> +    qdev_init_gpio_in (&s->busdev.qdev, ftapbbrg020_handle_req, 16);
> +    qdev_init_gpio_out(&s->busdev.qdev, s->ack, 16);
> +
> +    s->busy = -1;
> +    s->qtimer = qemu_new_timer_ns(vm_clock, ftapbbrg020_timer_tick, s);
> +    for (i = 0; i < 4; ++i) {
> +        ftapbbrg020_chan *c = s->chan + i;
> +        c->id   = i;
> +        c->chip = s;
> +    }
> +
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_ftapbbrg020 = {
> +    .name = "ftapbbrg020",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void ftapbbrg020_class_init(ObjectClass *klass, void *data)
> +{
> +    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +    DeviceClass *k = DEVICE_CLASS(klass);
> +
> +    sdc->init  = ftapbbrg020_init;
> +    k->vmsd    = &vmstate_ftapbbrg020;
> +    k->reset   = ftapbbrg020_reset;
> +    k->no_user = 1;
> +}
> +
> +static TypeInfo ftapbbrg020_info = {

const

> +    .name           = "ftapbbrg020",
> +    .parent         = TYPE_SYS_BUS_DEVICE,
> +    .instance_size  = sizeof(ftapbbrg020_state),
> +    .class_init     = ftapbbrg020_class_init,
> +};
> +
> +static void ftapbbrg020_register_types(void)
> +{
> +    type_register_static(&ftapbbrg020_info);
> +}
> +
> +type_init(ftapbbrg020_register_types)
> diff --git a/hw/ftapbbrg020.h b/hw/ftapbbrg020.h
> new file mode 100644
> index 0000000..0279f10
> --- /dev/null
> +++ b/hw/ftapbbrg020.h
> @@ -0,0 +1,43 @@
> +/*
> + *  arch/arm/mach-faraday/drivers/ftapbbrg020.h
> + *
> + *  Faraday FTAPBB020 APB Bridge with DMA function
> + *
> + *  Copyright (C) 2010 Faraday Technology
> + *  Copyright (C) 2012 Dante Su <address@hidden>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#ifndef __FTAPBBRG020_H
> +#define __FTAPBBRG020_H
> +
> +/*
> + * Channel base address
> + * @ch: channle id (0 <= id <= 3)
> + *      i.e. 0: Channel A
> + *           1: Channel B
> + *           2: Channel C
> + *           3: Channel D
> + */
> +#define REG_CHAN_ID(off)    (((off) - 0x80) >> 4)
> +#define REG_CHAN_BASE(ch)    (0x80 + ((ch) << 4))
> +
> +#define REG_CHAN_SRC        0x00
> +#define REG_CHAN_DST        0x04
> +#define REG_CHAN_CYC        0x08
> +#define REG_CHAN_CMD        0x0C
> +
> +#endif    /* __FTAPBB020_H */
> --
> 1.7.9.5
>
>
> ********************* Confidentiality Notice ************************
> This electronic message and any attachments may contain
> confidential and legally privileged information or
> information which is otherwise protected from disclosure.
> If you are not the intended recipient,please do not disclose
> the contents, either in whole or in part, to anyone,and
> immediately delete the message and any attachments from
> your computer system and destroy all hard copies.
> Thank you for your cooperation.
> ***********************************************************************
>
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]