On Fri, Jan 18, 2013 at 6:28 AM, Dante <dant...@faraday-tech.com> wrote: > Signed-off-by: Kuo-Jung Su <dant...@faraday-tech.com> > --- > 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 <dant...@faraday-tech.com> > + * > + * 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 <dant...@faraday-tech.com> > + * > + * 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. > *********************************************************************** > >