[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.
> ***********************************************************************
>
>
- [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support, Dante, 2013/01/18
- [Qemu-devel] [PATCH 02/18] hw: add QEMU model for Faraday APB DMA, Dante, 2013/01/18
- Re: [Qemu-devel] [PATCH 02/18] hw: add QEMU model for Faraday APB DMA,
Blue Swirl <=
- [Qemu-devel] [PATCH 03/18] hw: add QEMU model for Faraday AHB DMA, Dante, 2013/01/18
- [Qemu-devel] [PATCH 04/18] hw: add QEMU model for Faraday 1Gbps Ethernet MAC, Dante, 2013/01/18
- [Qemu-devel] [PATCH 05/18] hw: add QEMU model for Faraday 10/100Mbps Ethernet MAC, Dante, 2013/01/18
- Re: [Qemu-devel] [PATCH 01/18] hw: add Faraday a369 SoC platform support, Blue Swirl, 2013/01/19
- [Qemu-devel] [PATCH v2 00/20] Add Faraday A36x SoC platform support, Kuo-Jung Su, 2013/01/25
- [Qemu-devel] [PATCH v2 01/20] arm: add Faraday a36x SoC platform support, Kuo-Jung Su, 2013/01/25