From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:48195) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TTCF5-0005pD-86 for qemu-devel@nongnu.org; Tue, 30 Oct 2012 09:52:36 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TTCEy-0002Cx-9v for qemu-devel@nongnu.org; Tue, 30 Oct 2012 09:52:31 -0400 Received: from mailout2.w1.samsung.com ([210.118.77.12]:32127) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TTCEy-0002C1-00 for qemu-devel@nongnu.org; Tue, 30 Oct 2012 09:52:24 -0400 Received: from eusync4.samsung.com (mailout2.w1.samsung.com [210.118.77.12]) by mailout2.w1.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0MCP00BT7L7O8V90@mailout2.w1.samsung.com> for qemu-devel@nongnu.org; Tue, 30 Oct 2012 13:52:36 +0000 (GMT) Received: from [106.109.8.87] by eusync4.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0MCP00GFVL73WT80@eusync4.samsung.com> for qemu-devel@nongnu.org; Tue, 30 Oct 2012 13:52:18 +0000 (GMT) Message-id: <508FDB8F.3020806@samsung.com> Date: Tue, 30 Oct 2012 17:52:15 +0400 From: Igor Mitsyanko MIME-version: 1.0 References: <42188d2d7f0b49c9a715fb39c9f7489138e4d07b.1351560671.git.peter.crosthwaite@xilinx.com> In-reply-to: <42188d2d7f0b49c9a715fb39c9f7489138e4d07b.1351560671.git.peter.crosthwaite@xilinx.com> Content-type: text/plain; charset=ISO-8859-1; format=flowed Content-transfer-encoding: 7bit Subject: Re: [Qemu-devel] [PATCH v6 1/2] pl330: Initial version Reply-To: i.mitsyanko@samsung.com List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Peter Crosthwaite Cc: vineshp@xilinx.com, peter.maydell@linaro.org, qemu-devel@nongnu.org, Kirill Batuzov , john.williams@xilinx.com, edgar.iglesias@gmail.com, afaerber@suse.de On 10/30/2012 05:35 AM, Peter Crosthwaite wrote: > Device model for Primecell PL330 dma controller. > > Signed-off-by: Peter Crosthwaite > Signed-off-by: Kirill Batuzov > Tested-by: Igor Mitsyanko > + > +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