From: Igor Mitsyanko <i.mitsyanko@samsung.com>
To: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
Cc: vineshp@xilinx.com, peter.maydell@linaro.org,
qemu-devel@nongnu.org, Kirill Batuzov <batuzovk@ispras.ru>,
john.williams@xilinx.com, edgar.iglesias@gmail.com,
afaerber@suse.de
Subject: Re: [Qemu-devel] [PATCH v6 1/2] pl330: Initial version
Date: Tue, 30 Oct 2012 17:52:15 +0400 [thread overview]
Message-ID: <508FDB8F.3020806@samsung.com> (raw)
In-Reply-To: <42188d2d7f0b49c9a715fb39c9f7489138e4d07b.1351560671.git.peter.crosthwaite@xilinx.com>
On 10/30/2012 05:35 AM, Peter Crosthwaite wrote:
> Device model for Primecell PL330 dma controller.
>
> Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
> Signed-off-by: Kirill Batuzov <batuzovk@ispras.ru>
> Tested-by: Igor Mitsyanko <i.mitsyanko@samsung.com>
> +
> +static void pl330_debug_exec(PL330 *s)
> +{
> + uint8_t args[5];
> + uint8_t opcode;
> + uint8_t chan_id;
> + int i;
> + PL330Chan *ch;
> + const PL330InsnDesc *insn;
> +
> + s->debug_status = 1;
> + chan_id = (s->dbg[0] >> 8) & 0x07;
> + opcode = (s->dbg[0] >> 16) & 0xff;
> + args[0] = (s->dbg[0] >> 24) & 0xff;
> + args[1] = (s->dbg[1] >> 0) & 0xff;
> + args[2] = (s->dbg[1] >> 8) & 0xff;
> + args[3] = (s->dbg[1] >> 16) & 0xff;
> + args[4] = (s->dbg[1] >> 24) & 0xff;
> + DB_PRINT("chan id: %d\n", chan_id);
> + if (s->dbg[0] & 1) {
> + ch = &s->chan[chan_id];
> + } else {
> + ch = &s->manager;
> + }
> + insn = NULL;
> + for (i = 0; debug_insn_desc[i].size; i++) {
> + if ((opcode & debug_insn_desc[i].opmask) == debug_insn_desc[i].opcode) {
> + insn = &debug_insn_desc[i];
> + }
> + }
> + if (!insn) {
> + pl330_fault(ch, PL330_FAULT_UNDEF_INSTR | PL330_FAULT_DBG_INSTR);
> + return ;
> + }
> + ch->stall = 0;
> + insn->exec(ch, opcode, args, insn->size - 1);
> + if (ch->fault_type) {
> + ch->fault_type |= PL330_FAULT_DBG_INSTR;
> + }
> + if (ch->stall) {
> + qemu_log_mask(LOG_UNIMP, "pl330: stall of debug instruction not "
> + "implemented\n");
> + }
> + s->debug_status = 0;
> +}
> +
> +/* IOMEM mapped registers */
> +
> +static void pl330_iomem_write(void *opaque, hwaddr offset,
> + uint64_t value, unsigned size)
> +{
> + PL330 *s = (PL330 *) opaque;
> + uint32_t i;
> +
> + DB_PRINT("addr: %08x data: %08x\n", offset, (unsigned)value);
> +
This debug print (and another one in pl330_iomem_read() too) causes
compilation error if compiled with PL330_ERR_DEBUG defined
because of "offset" variable.
> + switch (offset) {
> + case PL330_REG_INTEN:
> + s->inten = value;
> + break;
> + case PL330_REG_INTCLR:
> + for (i = 0; i < s->num_events; i++) {
> + if (s->int_status & s->inten & value & (1 << i)) {
> + DB_PRINT("event interrupt lowered %d\n", i);
> + qemu_irq_lower(s->irq[i]);
> + }
> + }
> + s->ev_status &= ~(value & s->inten);
> + s->int_status &= ~(value & s->inten);
> + break;
> + case PL330_REG_DBGCMD:
> + if ((value & 3) == 0) {
> + pl330_debug_exec(s);
> + pl330_exec(s);
> + } else {
> + qemu_log_mask(LOG_GUEST_ERROR, "pl330: write of illegal value %u "
> + "for offset " TARGET_FMT_plx "\n", (unsigned)value,
> + offset);
> + }
> + break;
> + case PL330_REG_DBGINST0:
> + DB_PRINT("s->dbg[0] = %08x\n", (unsigned)value);
> + s->dbg[0] = value;
> + break;
> + case PL330_REG_DBGINST1:
> + DB_PRINT("s->dbg[1] = %08x\n", (unsigned)value);
> + s->dbg[1] = value;
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad write offset " TARGET_FMT_plx
> + "\n", offset);
> + break;
> + }
> +}
> +
> +static inline uint32_t pl330_iomem_read_imp(void *opaque,
> + hwaddr offset)
> +{
> + PL330 *s = (PL330 *)opaque;
> + int chan_id;
> + int i;
> + uint32_t res;
> +
> + if (offset >= PL330_REG_PERIPH_ID && offset < PL330_REG_PERIPH_ID + 32) {
> + return pl330_id[(offset - PL330_REG_PERIPH_ID) >> 2];
> + }
> + if (offset >= PL330_REG_CR0_BASE && offset < PL330_REG_CR0_BASE + 24) {
> + return s->cfg[(offset - PL330_REG_CR0_BASE) >> 2];
> + }
> + if (offset >= PL330_REG_CHANCTRL && offset < PL330_REG_DBGSTATUS) {
> + offset -= PL330_REG_CHANCTRL;
> + chan_id = offset >> 5;
> + if (chan_id >= s->num_chnls) {
> + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
> + TARGET_FMT_plx "\n", offset);
qemu_log_mask() is just logging, right? Then I guess you should "return;" here and after all other qemu_log_mask
() so switch that follows wouldn't be executed.
> + }
> + switch (offset & 0x1f) {
> + case 0x00:
> + return s->chan[chan_id].src;
> + case 0x04:
> + return s->chan[chan_id].dst;
> + case 0x08:
> + return s->chan[chan_id].control;
> + case 0x0C:
> + return s->chan[chan_id].lc[0];
> + case 0x10:
> + return s->chan[chan_id].lc[1];
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
> + TARGET_FMT_plx "\n", offset);
> + }
> + }
> + if (offset >= PL330_REG_CSR_BASE && offset < 0x400) {
> + offset -= PL330_REG_CSR_BASE;
> + chan_id = offset >> 3;
> + if (chan_id >= s->num_chnls) {
> + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
> + TARGET_FMT_plx "\n", offset);
> + }
> + switch ((offset >> 2) & 1) {
> + case 0x0:
> + res = (s->chan[chan_id].ns << 21) |
> + (s->chan[chan_id].wakeup << 4) |
> + (s->chan[chan_id].state) |
> + (s->chan[chan_id].wfp_sbp << 14);
> + return res;
> + case 0x1:
> + return s->chan[chan_id].pc;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR, "pl330: read error\n");
> + }
> + }
> + if (offset >= PL330_REG_FTR_BASE && offset < 0x100) {
> + offset -= PL330_REG_FTR_BASE;
> + chan_id = offset >> 2;
> + if (chan_id >= s->num_chnls) {
> + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
> + TARGET_FMT_plx "\n", offset);
> + }
> + return s->chan[chan_id].fault_type;
> + }
> + switch (offset) {
> + case PL330_REG_DSR:
> + return (s->manager.ns << 9) | (s->manager.wakeup << 4) |
> + (s->manager.state & 0xf);
> + case PL330_REG_DPC:
> + return s->manager.pc;
> + case PL330_REG_INTEN:
> + return s->inten;
> + case PL330_REG_INT_EVENT_RIS:
> + return s->ev_status;
> + case PL330_REG_INTMIS:
> + return s->int_status;
> + case PL330_REG_INTCLR:
> + /* Documentation says that we can't read this register
> + * but linux kernel does it
> + */
> + return 0;
> + case PL330_REG_FSRD:
> + return s->manager.state ? 1 : 0;
> + case PL330_REG_FSRC:
> + res = 0;
> + for (i = 0; i < s->num_chnls; i++) {
> + if (s->chan[i].state == pl330_chan_fault ||
> + s->chan[i].state == pl330_chan_fault_completing) {
> + res |= 1 << i;
> + }
> + }
> + return res;
> + case PL330_REG_FTRD:
> + return s->manager.fault_type;
> + case PL330_REG_DBGSTATUS:
> + return s->debug_status;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR, "pl330: bad read offset "
> + TARGET_FMT_plx "\n", offset);
> + }
> + return 0;
> +}
> +
> +static uint64_t pl330_iomem_read(void *opaque, hwaddr offset,
> + unsigned size)
> +{
> + int ret = pl330_iomem_read_imp(opaque, offset);
> + DB_PRINT("addr: %08x data: %08x\n", offset, ret);
> + return ret;
> +}
> +
> +static const MemoryRegionOps pl330_ops = {
> + .read = pl330_iomem_read,
> + .write = pl330_iomem_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .impl = {
> + .min_access_size = 4,
> + .max_access_size = 4,
> + }
> +};
> +
> +/* Controller logic and initialization */
> +
> +static void pl330_chan_reset(PL330Chan *ch)
> +{
> + ch->src = 0;
> + ch->dst = 0;
> + ch->pc = 0;
> + ch->state = pl330_chan_stopped;
> + ch->watchdog_timer = 0;
> + ch->stall = 0;
> + ch->control = 0;
> + ch->status = 0;
> + ch->fault_type = 0;
> +}
> +
> +static void pl330_reset(DeviceState *d)
> +{
> + int i;
> + PL330 *s = FROM_SYSBUS(PL330, sysbus_from_qdev(d));
> +
> + s->inten = 0;
> + s->int_status = 0;
> + s->ev_status = 0;
> + s->debug_status = 0;
> + s->num_faulting = 0;
> + s->manager.ns = s->mgr_ns_at_rst;
> + pl330_fifo_reset(&s->fifo);
> + pl330_queue_reset(&s->read_queue);
> + pl330_queue_reset(&s->write_queue);
> +
> + for (i = 0; i < s->num_chnls; i++) {
> + pl330_chan_reset(&s->chan[i]);
> + }
> + for (i = 0; i < s->num_periph_req; i++) {
> + s->periph_busy[i] = 0;
> + }
> +
> + qemu_del_timer(s->timer);
> +}
> +
> +static int pl330_init(SysBusDevice *dev)
> +{
> + int i;
> + PL330 *s = FROM_SYSBUS(PL330, dev);
> +
> + sysbus_init_irq(dev, &s->irq_abort);
> + memory_region_init_io(&s->iomem, &pl330_ops, s, "dma", PL330_IOMEM_SIZE);
> + sysbus_init_mmio(dev, &s->iomem);
> +
> + s->timer = qemu_new_timer_ns(vm_clock, pl330_exec_cycle_timer, s);
> +
> + s->cfg[0] = (s->mgr_ns_at_rst ? 0x4 : 0) |
> + (s->num_periph_req > 0 ? 1 : 0) |
> + ((s->num_chnls - 1) & 0x7) << 4 |
> + ((s->num_periph_req - 1) & 0x1f) << 12 |
> + ((s->num_events - 1) & 0x1f) << 17;
> +
> + switch (s->i_cache_len) {
> + case (4):
> + s->cfg[1] |= 2;
> + break;
> + case (8):
> + s->cfg[1] |= 3;
> + break;
> + case (16):
> + s->cfg[1] |= 4;
> + break;
> + case (32):
> + s->cfg[1] |= 5;
> + break;
> + default:
> + hw_error("Bad value for i-cache_len property: %d\n", s->i_cache_len);
> + }
> + s->cfg[1] |= ((s->num_i_cache_lines - 1) & 0xf) << 4;
> +
> + s->chan = g_new0(PL330Chan, s->num_chnls);
> + for (i = 0; i < s->num_chnls; i++) {
> + s->chan[i].parent = s;
> + s->chan[i].tag = (uint8_t)i;
> + }
> + s->manager.parent = s;
> + s->manager.tag = s->num_chnls;
> + s->manager.is_manager = true;
> +
> + s->irq = g_new0(qemu_irq, s->num_events);
> + for (i = 0; i < s->num_events; i++) {
> + sysbus_init_irq(dev, &s->irq[i]);
> + }
> +
> + qdev_init_gpio_in(&dev->qdev, pl330_dma_stop_irq, PL330_PERIPH_NUM);
> +
> + switch (s->data_width) {
> + case (32):
> + s->cfg[CFG_CRD] |= 0x2;
> + break;
> + case (64):
> + s->cfg[CFG_CRD] |= 0x3;
> + break;
> + case (128):
> + s->cfg[CFG_CRD] |= 0x4;
> + break;
> + default:
> + hw_error("Bad value for data_width property: %d\n", s->i_cache_len);
> + }
Wrong variable in hw_error.
> +
> + s->cfg[CFG_CRD] |= ((s->wr_cap - 1) & 0x7) << 4 |
> + ((s->wr_q_dep - 1) & 0xf) << 8 |
> + ((s->rd_cap - 1) & 0x7) << 12 |
> + ((s->rd_q_dep - 1) & 0xf) << 16 |
> + ((s->data_buffer_dep - 1) & 0x1ff) << 20;
> +
> + pl330_queue_init(&s->read_queue, s->rd_q_dep, s->num_chnls);
> + pl330_queue_init(&s->write_queue, s->wr_q_dep, s->num_chnls);
> + pl330_fifo_init(&s->fifo, s->data_buffer_dep);
> +
> + return 0;
> +}
> +
> +static Property pl330_properties[] = {
> + /* CR0 */
> + DEFINE_PROP_UINT8("num_chnls", PL330, num_chnls, 8),
> + DEFINE_PROP_UINT8("num_periph_req", PL330, num_periph_req, 8),
> + DEFINE_PROP_UINT8("num_events", PL330, num_events, 8),
> + DEFINE_PROP_UINT8("mgr_ns_at_rst", PL330, mgr_ns_at_rst, 0),
> + /* CR1 */
> + DEFINE_PROP_UINT8("i-cache_len", PL330, i_cache_len, 4),
> + DEFINE_PROP_UINT8("num_i-cache_lines", PL330, num_i_cache_lines, 8),
> + /* CR2-4 */
> + DEFINE_PROP_UINT32("boot_addr", PL330, cfg[CFG_BOOT_ADDR], 0),
> + DEFINE_PROP_UINT32("INS", PL330, cfg[CFG_INS], 0),
> + DEFINE_PROP_UINT32("PNS", PL330, cfg[CFG_PNS], 0),
> + /* CRD */
> + DEFINE_PROP_UINT8("data_width", PL330, data_width, 0),
> + DEFINE_PROP_UINT8("wr_cap", PL330, wr_cap, 0),
> + DEFINE_PROP_UINT8("wr_q_dep", PL330, wr_q_dep, 0),
> + DEFINE_PROP_UINT8("rd_cap", PL330, rd_cap, 0),
> + DEFINE_PROP_UINT8("rd_q_dep", PL330, rd_q_dep, 0),
> + DEFINE_PROP_UINT16("data_buffer_dep", PL330, data_buffer_dep, 0),
> +
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void pl330_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> + k->init = pl330_init;
> + dc->reset = pl330_reset;
> + dc->props = pl330_properties;
> + dc->vmsd = &vmstate_pl330;
> +}
> +
> +static const TypeInfo pl330_type_info = {
> + .name = "pl330",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(PL330),
> + .class_init = pl330_class_init,
> +};
> +
> +static void pl330_register_types(void)
> +{
> + type_register_static(&pl330_type_info);
> +}
> +
> +type_init(pl330_register_types)
--
Mitsyanko Igor
ASWG, Moscow R&D center, Samsung Electronics
email: i.mitsyanko@samsung.com
next prev parent reply other threads:[~2012-10-30 13:52 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-10-30 1:35 [Qemu-devel] [PATCH v6 0/2] Xilinx Zynq PL330 support Peter Crosthwaite
2012-10-30 1:35 ` [Qemu-devel] [PATCH v6 1/2] pl330: Initial version Peter Crosthwaite
2012-10-30 13:52 ` Igor Mitsyanko [this message]
2012-10-30 1:35 ` [Qemu-devel] [PATCH v6 2/2] xilinx_zynq: added pl330 to machine model Peter Crosthwaite
2012-10-30 19:01 ` Blue Swirl
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=508FDB8F.3020806@samsung.com \
--to=i.mitsyanko@samsung.com \
--cc=afaerber@suse.de \
--cc=batuzovk@ispras.ru \
--cc=edgar.iglesias@gmail.com \
--cc=john.williams@xilinx.com \
--cc=peter.crosthwaite@xilinx.com \
--cc=peter.maydell@linaro.org \
--cc=qemu-devel@nongnu.org \
--cc=vineshp@xilinx.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.