* [Qemu-devel] [PATCH 01/13] mxs/imx23: Add main header file
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2013-12-11 13:56 ` [Qemu-devel] [PATCH 02/13] mxs: Add CONFIG_MXS to the arm-softmmu config Michel Pollet
` (12 subsequent siblings)
13 siblings, 0 replies; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
Header file containing most of the base addresses and IO registers
needed for the Freescale mxs/imx23 SoC emumation.
Also contains a generic helper to implement the SET/AND/OR/XOR trick
shared by pretty much all of the IO blocks on this SoC
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/arm/mxs.h | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 208 insertions(+)
create mode 100644 hw/arm/mxs.h
diff --git a/hw/arm/mxs.h b/hw/arm/mxs.h
new file mode 100644
index 0000000..91d3ddb
--- /dev/null
+++ b/hw/arm/mxs.h
@@ -0,0 +1,208 @@
+/*
+ * mxs.h
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence, the bits that aren't from linux's
+ */
+/*
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MXS_H_
+#define MXS_H_
+
+/*
+ * OCRAM
+ */
+#define MX23_OCRAM_BASE_ADDR 0x00000000
+#define MX23_OCRAM_SIZE SZ_32K
+
+/*
+ * IO
+ */
+#define MX23_IO_BASE_ADDR 0x80000000
+#define MX23_IO_SIZE SZ_1M
+
+#define MX23_ICOLL_BASE_ADDR (MX23_IO_BASE_ADDR + 0x000000)
+#define MX23_APBH_DMA_BASE_ADDR (MX23_IO_BASE_ADDR + 0x004000)
+#define MX23_BCH_BASE_ADDR (MX23_IO_BASE_ADDR + 0x00a000)
+#define MX23_GPMI_BASE_ADDR (MX23_IO_BASE_ADDR + 0x00c000)
+#define MX23_SSP1_BASE_ADDR (MX23_IO_BASE_ADDR + 0x010000)
+#define MX23_PINCTRL_BASE_ADDR (MX23_IO_BASE_ADDR + 0x018000)
+#define MX23_DIGCTL_BASE_ADDR (MX23_IO_BASE_ADDR + 0x01c000)
+#define MX23_ETM_BASE_ADDR (MX23_IO_BASE_ADDR + 0x020000)
+#define MX23_APBX_DMA_BASE_ADDR (MX23_IO_BASE_ADDR + 0x024000)
+#define MX23_DCP_BASE_ADDR (MX23_IO_BASE_ADDR + 0x028000)
+#define MX23_PXP_BASE_ADDR (MX23_IO_BASE_ADDR + 0x02a000)
+#define MX23_OCOTP_BASE_ADDR (MX23_IO_BASE_ADDR + 0x02c000)
+#define MX23_AXI_AHB0_BASE_ADDR (MX23_IO_BASE_ADDR + 0x02e000)
+#define MX23_LCDIF_BASE_ADDR (MX23_IO_BASE_ADDR + 0x030000)
+#define MX23_SSP2_BASE_ADDR (MX23_IO_BASE_ADDR + 0x034000)
+#define MX23_TVENC_BASE_ADDR (MX23_IO_BASE_ADDR + 0x038000)
+#define MX23_CLKCTRL_BASE_ADDR (MX23_IO_BASE_ADDR + 0x040000)
+#define MX23_SAIF0_BASE_ADDR (MX23_IO_BASE_ADDR + 0x042000)
+#define MX23_POWER_BASE_ADDR (MX23_IO_BASE_ADDR + 0x044000)
+#define MX23_SAIF1_BASE_ADDR (MX23_IO_BASE_ADDR + 0x046000)
+#define MX23_AUDIOOUT_BASE_ADDR (MX23_IO_BASE_ADDR + 0x048000)
+#define MX23_AUDIOIN_BASE_ADDR (MX23_IO_BASE_ADDR + 0x04c000)
+#define MX23_LRADC_BASE_ADDR (MX23_IO_BASE_ADDR + 0x050000)
+#define MX23_SPDIF_BASE_ADDR (MX23_IO_BASE_ADDR + 0x054000)
+#define MX23_I2C_BASE_ADDR (MX23_IO_BASE_ADDR + 0x058000)
+#define MX23_RTC_BASE_ADDR (MX23_IO_BASE_ADDR + 0x05c000)
+#define MX23_PWM_BASE_ADDR (MX23_IO_BASE_ADDR + 0x064000)
+#define MX23_TIMROT_BASE_ADDR (MX23_IO_BASE_ADDR + 0x068000)
+#define MX23_AUART1_BASE_ADDR (MX23_IO_BASE_ADDR + 0x06c000)
+#define MX23_AUART2_BASE_ADDR (MX23_IO_BASE_ADDR + 0x06e000)
+#define MX23_DUART_BASE_ADDR (MX23_IO_BASE_ADDR + 0x070000)
+#define MX23_USBPHY_BASE_ADDR (MX23_IO_BASE_ADDR + 0x07c000)
+#define MX23_USBCTRL_BASE_ADDR (MX23_IO_BASE_ADDR + 0x080000)
+#define MX23_DRAM_BASE_ADDR (MX23_IO_BASE_ADDR + 0x0e0000)
+
+#define MX23_IO_P2V(x) MXS_IO_P2V(x)
+#define MX23_IO_ADDRESS(x) IOMEM(MX23_IO_P2V(x))
+
+/*
+ * IRQ
+ */
+#define MX23_INT_DUART 0
+#define MX23_INT_COMMS_RX 1
+#define MX23_INT_COMMS_TX 1
+#define MX23_INT_SSP2_ERROR 2
+#define MX23_INT_VDD5V 3
+#define MX23_INT_HEADPHONE_SHORT 4
+#define MX23_INT_DAC_DMA 5
+#define MX23_INT_DAC_ERROR 6
+#define MX23_INT_ADC_DMA 7
+#define MX23_INT_ADC_ERROR 8
+#define MX23_INT_SPDIF_DMA 9
+#define MX23_INT_SAIF2_DMA 9
+#define MX23_INT_SPDIF_ERROR 10
+#define MX23_INT_SAIF1_IRQ 10
+#define MX23_INT_SAIF2_IRQ 10
+#define MX23_INT_USB_CTRL 11
+#define MX23_INT_USB_WAKEUP 12
+#define MX23_INT_GPMI_DMA 13
+#define MX23_INT_SSP1_DMA 14
+#define MX23_INT_SSP1_ERROR 15
+#define MX23_INT_GPIO0 16
+#define MX23_INT_GPIO1 17
+#define MX23_INT_GPIO2 18
+#define MX23_INT_SAIF1_DMA 19
+#define MX23_INT_SSP2_DMA 20
+#define MX23_INT_ECC8_IRQ 21
+#define MX23_INT_RTC_ALARM 22
+#define MX23_INT_AUART1_TX_DMA 23
+#define MX23_INT_AUART1 24
+#define MX23_INT_AUART1_RX_DMA 25
+#define MX23_INT_I2C_DMA 26
+#define MX23_INT_I2C_ERROR 27
+#define MX23_INT_TIMER0 28
+#define MX23_INT_TIMER1 29
+#define MX23_INT_TIMER2 30
+#define MX23_INT_TIMER3 31
+#define MX23_INT_BATT_BRNOUT 32
+#define MX23_INT_VDDD_BRNOUT 33
+#define MX23_INT_VDDIO_BRNOUT 34
+#define MX23_INT_VDD18_BRNOUT 35
+#define MX23_INT_TOUCH_DETECT 36
+#define MX23_INT_LRADC_CH0 37
+#define MX23_INT_LRADC_CH1 38
+#define MX23_INT_LRADC_CH2 39
+#define MX23_INT_LRADC_CH3 40
+#define MX23_INT_LRADC_CH4 41
+#define MX23_INT_LRADC_CH5 42
+#define MX23_INT_LRADC_CH6 43
+#define MX23_INT_LRADC_CH7 44
+#define MX23_INT_LCDIF_DMA 45
+#define MX23_INT_LCDIF_ERROR 46
+#define MX23_INT_DIGCTL_DEBUG_TRAP 47
+#define MX23_INT_RTC_1MSEC 48
+#define MX23_INT_DRI_DMA 49
+#define MX23_INT_DRI_ATTENTION 50
+#define MX23_INT_GPMI_ATTENTION 51
+#define MX23_INT_IR 52
+#define MX23_INT_DCP_VMI 53
+#define MX23_INT_DCP 54
+#define MX23_INT_BCH 56
+#define MX23_INT_PXP 57
+#define MX23_INT_AUART2_TX_DMA 58
+#define MX23_INT_AUART2 59
+#define MX23_INT_AUART2_RX_DMA 60
+#define MX23_INT_VDAC_DETECT 61
+#define MX23_INT_VDD5V_DROOP 64
+#define MX23_INT_DCDC4P2_BO 65
+
+/*
+ * APBH DMA
+ */
+#define MX23_DMA_SSP1 1
+#define MX23_DMA_SSP2 2
+#define MX23_DMA_GPMI0 4
+#define MX23_DMA_GPMI1 5
+#define MX23_DMA_GPMI2 6
+#define MX23_DMA_GPMI3 7
+
+/*
+ * APBX DMA
+ */
+#define MX23_DMA_ADC 0
+#define MX23_DMA_DAC 1
+#define MX23_DMA_SPDIF 2
+#define MX23_DMA_I2C 3
+#define MX23_DMA_SAIF0 4
+#define MX23_DMA_UART0_RX 6
+#define MX23_DMA_UART0_TX 7
+#define MX23_DMA_UART1_RX 8
+#define MX23_DMA_UART1_TX 9
+#define MX23_DMA_SAIF1 10
+
+/*
+ * This is a generic function that handles write to IO register adresses
+ * This method of writing to peripherals IOs is shared by all the specific
+ * mxs IO blocks, thus this shared inline helper.
+ */
+static inline uint32_t mxs_write(
+ uint32_t * dst, hwaddr offset,
+ uint32_t value, unsigned size)
+{
+ if (!dst) {
+ return 0;
+ }
+ uint32_t oldvalue = *dst;
+ switch (offset & 0xf) {
+ case 0:
+ *dst = value;
+ break;
+ case 4:
+ *dst |= value;
+ break;
+ case 8:
+ *dst &= ~value;
+ break;
+ case 0xc:
+ *dst ^= value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ return oldvalue;
+}
+
+#endif /* MXS_H_ */
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 02/13] mxs: Add CONFIG_MXS to the arm-softmmu config
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
2013-12-11 13:56 ` [Qemu-devel] [PATCH 01/13] mxs/imx23: Add main header file Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2014-01-06 15:08 ` Peter Maydell
2013-12-11 13:56 ` [Qemu-devel] [PATCH 03/13] mxs/imx23: Add uart driver Michel Pollet
` (11 subsequent siblings)
13 siblings, 1 reply; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
Allows selective compilation of the MXS bits
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
default-configs/arm-softmmu.mak | 1 +
1 file changed, 1 insertion(+)
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index e48f102..0d4cf95 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -71,6 +71,7 @@ CONFIG_BLIZZARD=y
CONFIG_ONENAND=y
CONFIG_TUSB6010=y
CONFIG_IMX=y
+CONFIG_MXS=y
CONFIG_MAINSTONE=y
CONFIG_NSERIES=y
CONFIG_REALVIEW=y
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 02/13] mxs: Add CONFIG_MXS to the arm-softmmu config
2013-12-11 13:56 ` [Qemu-devel] [PATCH 02/13] mxs: Add CONFIG_MXS to the arm-softmmu config Michel Pollet
@ 2014-01-06 15:08 ` Peter Maydell
0 siblings, 0 replies; 36+ messages in thread
From: Peter Maydell @ 2014-01-06 15:08 UTC (permalink / raw)
To: Michel Pollet; +Cc: QEMU Developers
On 11 December 2013 13:56, Michel Pollet <buserror@gmail.com> wrote:
> Allows selective compilation of the MXS bits
>
> Signed-off-by: Michel Pollet <buserror@gmail.com>
> ---
> default-configs/arm-softmmu.mak | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index e48f102..0d4cf95 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -71,6 +71,7 @@ CONFIG_BLIZZARD=y
> CONFIG_ONENAND=y
> CONFIG_TUSB6010=y
> CONFIG_IMX=y
> +CONFIG_MXS=y
> CONFIG_MAINSTONE=y
> CONFIG_NSERIES=y
> CONFIG_REALVIEW=y
In general we prefer to have a CONFIG_ switch for
each device (and then one for the board as well);
see for example the CONFIG_ALLWINNER_ switches just
added. So I think it would be better to add those
in the patches which add each device, rather than
just having this one here.
thanks
-- PMM
^ permalink raw reply [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 03/13] mxs/imx23: Add uart driver
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
2013-12-11 13:56 ` [Qemu-devel] [PATCH 01/13] mxs/imx23: Add main header file Michel Pollet
2013-12-11 13:56 ` [Qemu-devel] [PATCH 02/13] mxs: Add CONFIG_MXS to the arm-softmmu config Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2014-01-06 15:19 ` Peter Maydell
2013-12-11 13:56 ` [Qemu-devel] [PATCH 04/13] mxs/imx23: Add DMA driver Michel Pollet
` (10 subsequent siblings)
13 siblings, 1 reply; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
Prototype driver for the mxs/imx23 uart IO block. This has no
real 'uart' functional code, apart from letting itself be
initialized by linux without generating a timeout error.
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/char/Makefile.objs | 1 +
hw/char/mxs_uart.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 147 insertions(+)
create mode 100644 hw/char/mxs_uart.c
diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
index cbd6a00..8ea5670 100644
--- a/hw/char/Makefile.objs
+++ b/hw/char/Makefile.objs
@@ -19,6 +19,7 @@ common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
common-obj-$(CONFIG_GRLIB) += grlib_apbuart.o
common-obj-$(CONFIG_IMX) += imx_serial.o
+common-obj-$(CONFIG_MXS) += mxs_uart.o
common-obj-$(CONFIG_LM32) += lm32_juart.o
common-obj-$(CONFIG_LM32) += lm32_uart.o
common-obj-$(CONFIG_MILKYMIST) += milkymist-uart.o
diff --git a/hw/char/mxs_uart.c b/hw/char/mxs_uart.c
new file mode 100644
index 0000000..79b2582
--- /dev/null
+++ b/hw/char/mxs_uart.c
@@ -0,0 +1,146 @@
+/*
+ * mxs_uart.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ */
+
+/*
+ * Work in progress ! Right now there's just enough so that linux driver
+ * will instantiate after a probe, there is no functional code.
+ */
+#include "hw/sysbus.h"
+#include "hw/arm/mxs.h"
+
+#define D(w) w
+
+enum {
+ UART_CTRL = 0x0,
+ UART_CTRL1 = 0x1,
+ UART_CTRL2 = 0x2,
+ UART_LINECTRL = 0x3,
+ UART_LINECTRL2 = 0x4,
+ UART_INTR = 0x5,
+ UART_APP_DATA = 0x6,
+ UART_APP_STAT = 0x7,
+ UART_APP_DEBUG = 0x8,
+ UART_APP_VERSION = 0x9,
+ UART_APP_AUTOBAUD = 0xa,
+
+ UART_MAX,
+};
+typedef struct mxs_uart_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t r[UART_MAX];
+
+ struct {
+ uint16_t b[16];
+ int w, r;
+ } fifo[2];
+ qemu_irq irq;
+ CharDriverState *chr;
+} mxs_uart_state;
+
+static uint64_t mxs_uart_read(
+ void *opaque, hwaddr offset, unsigned size)
+{
+ mxs_uart_state *s = (mxs_uart_state *) opaque;
+ uint32_t res = 0;
+
+ D(printf("%s %04x (%d) = ", __func__, (int)offset, size);)
+ switch (offset >> 4) {
+ case 0 ... UART_MAX:
+ res = s->r[offset >> 4];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ D(printf("%08x\n", res);)
+
+ return res;
+}
+
+static void mxs_uart_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ mxs_uart_state *s = (mxs_uart_state *) opaque;
+ uint32_t oldvalue = 0;
+
+ D(printf("%s %04x %08x(%d)\n", __func__, (int)offset, (int)value, size);)
+ switch (offset >> 4) {
+ case 0 ... UART_MAX:
+ mxs_write(&s->r[offset >> 4], offset, value, size);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ switch (offset >> 4) {
+ case UART_CTRL:
+ if ((oldvalue ^ s->r[UART_CTRL]) == 0x80000000
+ && !(oldvalue & 0x80000000)) {
+ printf("%s reseting, anding clockgate\n", __func__);
+ s->r[UART_CTRL] |= 0x40000000;
+ }
+ break;
+ }
+}
+
+static void mxs_uart_set_irq(void *opaque, int irq, int level)
+{
+// mxs_uart_state *s = (mxs_uart_state *)opaque;
+ printf("%s %3d = %d\n", __func__, irq, level);
+}
+
+static const MemoryRegionOps mxs_uart_ops = {
+ .read = mxs_uart_read,
+ .write = mxs_uart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+
+static int mxs_uart_init(SysBusDevice *dev)
+{
+ mxs_uart_state *s = OBJECT_CHECK(mxs_uart_state, dev, "mxs_uart");
+ DeviceState *qdev = DEVICE(dev);
+
+ qdev_init_gpio_in(qdev, mxs_uart_set_irq, 32 * 3);
+ sysbus_init_irq(dev, &s->irq);
+ memory_region_init_io(&s->iomem, OBJECT(s), &mxs_uart_ops, s, "mxs_uart", 0x2000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ s->r[UART_CTRL] = 0xc0030000;
+ s->r[UART_CTRL2] = 0x00220180;
+ s->r[UART_APP_STAT] = 0x89f00000;
+ s->r[UART_APP_VERSION] = 0x03000000;
+ return 0;
+}
+
+
+static void mxs_uart_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = mxs_uart_init;
+}
+
+static TypeInfo uart_info = {
+ .name = "mxs_uart",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mxs_uart_state),
+ .class_init = mxs_uart_class_init,
+};
+
+static void mxs_uart_register(void)
+{
+ type_register_static(&uart_info);
+}
+
+type_init(mxs_uart_register)
+
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 03/13] mxs/imx23: Add uart driver
2013-12-11 13:56 ` [Qemu-devel] [PATCH 03/13] mxs/imx23: Add uart driver Michel Pollet
@ 2014-01-06 15:19 ` Peter Maydell
2014-01-11 7:39 ` Peter Crosthwaite
0 siblings, 1 reply; 36+ messages in thread
From: Peter Maydell @ 2014-01-06 15:19 UTC (permalink / raw)
To: Michel Pollet; +Cc: QEMU Developers
On 11 December 2013 13:56, Michel Pollet <buserror@gmail.com> wrote:
> Prototype driver for the mxs/imx23 uart IO block. This has no
> real 'uart' functional code, apart from letting itself be
> initialized by linux without generating a timeout error.
>
> Signed-off-by: Michel Pollet <buserror@gmail.com>
Hi; there are some minor code style/formatting errors
with this patch. You can catch these by running the
scripts/checkpatch.pl script on your patches. (It
doesn't catch everything, and sometimes it gets
confused and gives bogus results, but it's a good
sanity check.)
> ---
> hw/char/Makefile.objs | 1 +
> hw/char/mxs_uart.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 147 insertions(+)
> create mode 100644 hw/char/mxs_uart.c
>
> diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
> index cbd6a00..8ea5670 100644
> --- a/hw/char/Makefile.objs
> +++ b/hw/char/Makefile.objs
> @@ -19,6 +19,7 @@ common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
> common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
> common-obj-$(CONFIG_GRLIB) += grlib_apbuart.o
> common-obj-$(CONFIG_IMX) += imx_serial.o
> +common-obj-$(CONFIG_MXS) += mxs_uart.o
This should be a CONFIG_MXS_UART (see remark on earlier patch).
> common-obj-$(CONFIG_LM32) += lm32_juart.o
> common-obj-$(CONFIG_LM32) += lm32_uart.o
> common-obj-$(CONFIG_MILKYMIST) += milkymist-uart.o
> diff --git a/hw/char/mxs_uart.c b/hw/char/mxs_uart.c
> new file mode 100644
> index 0000000..79b2582
> --- /dev/null
> +++ b/hw/char/mxs_uart.c
> @@ -0,0 +1,146 @@
> +/*
> + * mxs_uart.c
> + *
> + * Copyright: Michel Pollet <buserror@gmail.com>
> + *
> + * QEMU Licence
This is too vague. If you mean GPLv2 please say so.
> + */
> +
> +/*
> + * Work in progress ! Right now there's just enough so that linux driver
> + * will instantiate after a probe, there is no functional code.
> + */
> +#include "hw/sysbus.h"
> +#include "hw/arm/mxs.h"
> +
> +#define D(w) w
Please get rid of this. You can use a similar DPRINTF
type macro as other devices do, or no debug tracing at
all, as you wish.
> +
> +enum {
> + UART_CTRL = 0x0,
> + UART_CTRL1 = 0x1,
> + UART_CTRL2 = 0x2,
> + UART_LINECTRL = 0x3,
> + UART_LINECTRL2 = 0x4,
> + UART_INTR = 0x5,
> + UART_APP_DATA = 0x6,
> + UART_APP_STAT = 0x7,
> + UART_APP_DEBUG = 0x8,
> + UART_APP_VERSION = 0x9,
> + UART_APP_AUTOBAUD = 0xa,
> +
> + UART_MAX,
> +};
> +typedef struct mxs_uart_state {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> +
> + uint32_t r[UART_MAX];
> +
> + struct {
> + uint16_t b[16];
> + int w, r;
> + } fifo[2];
> + qemu_irq irq;
> + CharDriverState *chr;
> +} mxs_uart_state;
Structured type names should be in CamelCase;
see CODING_STYLE.
> +static uint64_t mxs_uart_read(
> + void *opaque, hwaddr offset, unsigned size)
> +{
> + mxs_uart_state *s = (mxs_uart_state *) opaque;
> + uint32_t res = 0;
> +
> + D(printf("%s %04x (%d) = ", __func__, (int)offset, size);)
> + switch (offset >> 4) {
> + case 0 ... UART_MAX:
This indent is wrong, as checkpatch.pl will tell you.
> + res = s->r[offset >> 4];
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> + D(printf("%08x\n", res);)
> +
> + return res;
> +}
> +
> +static void mxs_uart_write(void *opaque, hwaddr offset,
> + uint64_t value, unsigned size)
> +{
> + mxs_uart_state *s = (mxs_uart_state *) opaque;
> + uint32_t oldvalue = 0;
> +
> + D(printf("%s %04x %08x(%d)\n", __func__, (int)offset, (int)value, size);)
> + switch (offset >> 4) {
> + case 0 ... UART_MAX:
> + mxs_write(&s->r[offset >> 4], offset, value, size);
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> + switch (offset >> 4) {
> + case UART_CTRL:
> + if ((oldvalue ^ s->r[UART_CTRL]) == 0x80000000
> + && !(oldvalue & 0x80000000)) {
> + printf("%s reseting, anding clockgate\n", __func__);
Stray debug printout.
> + s->r[UART_CTRL] |= 0x40000000;
> + }
> + break;
> + }
> +}
> +
> +static void mxs_uart_set_irq(void *opaque, int irq, int level)
> +{
> +// mxs_uart_state *s = (mxs_uart_state *)opaque;
Don't leave commented out code in your patches, please.
> + printf("%s %3d = %d\n", __func__, irq, level);
> +}
> +
> +static const MemoryRegionOps mxs_uart_ops = {
> + .read = mxs_uart_read,
> + .write = mxs_uart_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +
> +static int mxs_uart_init(SysBusDevice *dev)
> +{
> + mxs_uart_state *s = OBJECT_CHECK(mxs_uart_state, dev, "mxs_uart");
> + DeviceState *qdev = DEVICE(dev);
> +
> + qdev_init_gpio_in(qdev, mxs_uart_set_irq, 32 * 3);
Why has a UART got so many inbound GPIO signals?
At minimum, there should be a comment here describing
what they are.
> + sysbus_init_irq(dev, &s->irq);
> + memory_region_init_io(&s->iomem, OBJECT(s), &mxs_uart_ops, s, "mxs_uart", 0x2000);
> + sysbus_init_mmio(dev, &s->iomem);
> +
> + s->r[UART_CTRL] = 0xc0030000;
> + s->r[UART_CTRL2] = 0x00220180;
> + s->r[UART_APP_STAT] = 0x89f00000;
> + s->r[UART_APP_VERSION] = 0x03000000;
Don't do reset here, do it in a reset function (which you
set up by setting the DeviceClass reset function pointer
in the class init function). Reset is called for you after
init, so you don't need to do any reset in init.
> + return 0;
> +}
> +
> +
> +static void mxs_uart_class_init(ObjectClass *klass, void *data)
> +{
> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +
> + sdc->init = mxs_uart_init;
You need a vmstate structure to support save/load
and VM migration, and then to set the DeviceClass
vmsd field to point to it here.
> +}
> +
> +static TypeInfo uart_info = {
> + .name = "mxs_uart",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(mxs_uart_state),
> + .class_init = mxs_uart_class_init,
> +};
> +
> +static void mxs_uart_register(void)
> +{
> + type_register_static(&uart_info);
> +}
> +
> +type_init(mxs_uart_register)
> +
thanks
-- PMM
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 03/13] mxs/imx23: Add uart driver
2014-01-06 15:19 ` Peter Maydell
@ 2014-01-11 7:39 ` Peter Crosthwaite
0 siblings, 0 replies; 36+ messages in thread
From: Peter Crosthwaite @ 2014-01-11 7:39 UTC (permalink / raw)
To: Peter Maydell; +Cc: Michel Pollet, QEMU Developers
On Tue, Jan 7, 2014 at 1:19 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 11 December 2013 13:56, Michel Pollet <buserror@gmail.com> wrote:
>> Prototype driver for the mxs/imx23 uart IO block. This has no
>> real 'uart' functional code, apart from letting itself be
>> initialized by linux without generating a timeout error.
>>
>> Signed-off-by: Michel Pollet <buserror@gmail.com>
>
> Hi; there are some minor code style/formatting errors
> with this patch. You can catch these by running the
> scripts/checkpatch.pl script on your patches. (It
> doesn't catch everything, and sometimes it gets
> confused and gives bogus results, but it's a good
> sanity check.)
>
>> ---
>> hw/char/Makefile.objs | 1 +
>> hw/char/mxs_uart.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 147 insertions(+)
>> create mode 100644 hw/char/mxs_uart.c
>>
>> diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
>> index cbd6a00..8ea5670 100644
>> --- a/hw/char/Makefile.objs
>> +++ b/hw/char/Makefile.objs
>> @@ -19,6 +19,7 @@ common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
>> common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
>> common-obj-$(CONFIG_GRLIB) += grlib_apbuart.o
>> common-obj-$(CONFIG_IMX) += imx_serial.o
>> +common-obj-$(CONFIG_MXS) += mxs_uart.o
>
> This should be a CONFIG_MXS_UART (see remark on earlier patch).
>
>> common-obj-$(CONFIG_LM32) += lm32_juart.o
>> common-obj-$(CONFIG_LM32) += lm32_uart.o
>> common-obj-$(CONFIG_MILKYMIST) += milkymist-uart.o
>> diff --git a/hw/char/mxs_uart.c b/hw/char/mxs_uart.c
>> new file mode 100644
>> index 0000000..79b2582
>> --- /dev/null
>> +++ b/hw/char/mxs_uart.c
>> @@ -0,0 +1,146 @@
>> +/*
>> + * mxs_uart.c
>> + *
>> + * Copyright: Michel Pollet <buserror@gmail.com>
>> + *
>> + * QEMU Licence
>
> This is too vague. If you mean GPLv2 please say so.
>
>> + */
>> +
>> +/*
>> + * Work in progress ! Right now there's just enough so that linux driver
>> + * will instantiate after a probe, there is no functional code.
>> + */
>> +#include "hw/sysbus.h"
>> +#include "hw/arm/mxs.h"
>> +
>> +#define D(w) w
>
> Please get rid of this. You can use a similar DPRINTF
> type macro as other devices do, or no debug tracing at
> all, as you wish.
>
To clarify further, make DPRINTF use a regular c-code if, rather than
conditional compilation. The reason being your debug printfery should
always be compile tested.
>> +
>> +enum {
>> + UART_CTRL = 0x0,
>> + UART_CTRL1 = 0x1,
>> + UART_CTRL2 = 0x2,
>> + UART_LINECTRL = 0x3,
>> + UART_LINECTRL2 = 0x4,
>> + UART_INTR = 0x5,
>> + UART_APP_DATA = 0x6,
>> + UART_APP_STAT = 0x7,
>> + UART_APP_DEBUG = 0x8,
>> + UART_APP_VERSION = 0x9,
>> + UART_APP_AUTOBAUD = 0xa,
>> +
>> + UART_MAX,
>> +};
>> +typedef struct mxs_uart_state {
>> + SysBusDevice busdev;
>> + MemoryRegion iomem;
Check QOM conventions (I commented in other patch - see for details).
>> +
>> + uint32_t r[UART_MAX];
>> +
>> + struct {
>> + uint16_t b[16];
>> + int w, r;
>> + } fifo[2];
>> + qemu_irq irq;
>> + CharDriverState *chr;
Dead variable. Just add it along with your functionality. Although the
functionality would help a lot. Its a bit of a trap, advertisiting a
UART which is just a NOP. Some qemu_log_mask(LOG_UNIMP, at various
places migt be in order, although depending on complexity it may not
be much harder to get basic txrx going.
>> +} mxs_uart_state;
>
> Structured type names should be in CamelCase;
> see CODING_STYLE.
>
>> +static uint64_t mxs_uart_read(
>> + void *opaque, hwaddr offset, unsigned size)
>> +{
>> + mxs_uart_state *s = (mxs_uart_state *) opaque;
>> + uint32_t res = 0;
>> +
>> + D(printf("%s %04x (%d) = ", __func__, (int)offset, size);)
>> + switch (offset >> 4) {
>> + case 0 ... UART_MAX:
>
> This indent is wrong, as checkpatch.pl will tell you.
>
>> + res = s->r[offset >> 4];
>> + break;
>> + default:
>> + qemu_log_mask(LOG_GUEST_ERROR,
>> + "%s: bad offset 0x%x\n", __func__, (int) offset);
>> + break;
>> + }
>> + D(printf("%08x\n", res);)
>> +
>> + return res;
>> +}
>> +
>> +static void mxs_uart_write(void *opaque, hwaddr offset,
>> + uint64_t value, unsigned size)
>> +{
>> + mxs_uart_state *s = (mxs_uart_state *) opaque;
>> + uint32_t oldvalue = 0;
>> +
>> + D(printf("%s %04x %08x(%d)\n", __func__, (int)offset, (int)value, size);)
>> + switch (offset >> 4) {
>> + case 0 ... UART_MAX:
>> + mxs_write(&s->r[offset >> 4], offset, value, size);
>> + break;
>> + default:
>> + qemu_log_mask(LOG_GUEST_ERROR,
>> + "%s: bad offset 0x%x\n", __func__, (int) offset);
>> + break;
>> + }
>> + switch (offset >> 4) {
>> + case UART_CTRL:
>> + if ((oldvalue ^ s->r[UART_CTRL]) == 0x80000000
>> + && !(oldvalue & 0x80000000)) {
>> + printf("%s reseting, anding clockgate\n", __func__);
>
> Stray debug printout.
>
>> + s->r[UART_CTRL] |= 0x40000000;
>> + }
>> + break;
>> + }
>> +}
>> +
>> +static void mxs_uart_set_irq(void *opaque, int irq, int level)
>> +{
>> +// mxs_uart_state *s = (mxs_uart_state *)opaque;
>
> Don't leave commented out code in your patches, please.
>
>> + printf("%s %3d = %d\n", __func__, irq, level);
>> +}
>> +
>> +static const MemoryRegionOps mxs_uart_ops = {
>> + .read = mxs_uart_read,
>> + .write = mxs_uart_write,
>> + .endianness = DEVICE_NATIVE_ENDIAN,
>> +};
>> +
>> +
>> +static int mxs_uart_init(SysBusDevice *dev)
>> +{
>> + mxs_uart_state *s = OBJECT_CHECK(mxs_uart_state, dev, "mxs_uart");
>> + DeviceState *qdev = DEVICE(dev);
>> +
>> + qdev_init_gpio_in(qdev, mxs_uart_set_irq, 32 * 3);
>
> Why has a UART got so many inbound GPIO signals?
> At minimum, there should be a comment here describing
> what they are.
>
>> + sysbus_init_irq(dev, &s->irq);
>> + memory_region_init_io(&s->iomem, OBJECT(s), &mxs_uart_ops, s, "mxs_uart", 0x2000);
>> + sysbus_init_mmio(dev, &s->iomem);
>> +
>> + s->r[UART_CTRL] = 0xc0030000;
>> + s->r[UART_CTRL2] = 0x00220180;
>> + s->r[UART_APP_STAT] = 0x89f00000;
>> + s->r[UART_APP_VERSION] = 0x03000000;
>
> Don't do reset here, do it in a reset function (which you
> set up by setting the DeviceClass reset function pointer
> in the class init function). Reset is called for you after
> init, so you don't need to do any reset in init.
>
>> + return 0;
>> +}
>> +
>> +
>> +static void mxs_uart_class_init(ObjectClass *klass, void *data)
>> +{
>> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
>> +
>> + sdc->init = mxs_uart_init;
>
> You need a vmstate structure to support save/load
> and VM migration, and then to set the DeviceClass
> vmsd field to point to it here.
>
>> +}
>> +
>> +static TypeInfo uart_info = {
>> + .name = "mxs_uart",
>> + .parent = TYPE_SYS_BUS_DEVICE,
>> + .instance_size = sizeof(mxs_uart_state),
>> + .class_init = mxs_uart_class_init,
>> +};
>> +
>> +static void mxs_uart_register(void)
>> +{
>> + type_register_static(&uart_info);
>> +}
>> +
>> +type_init(mxs_uart_register)
>> +
Stray blank line at EOF.
Regards,
Peter
>
> thanks
> -- PMM
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 04/13] mxs/imx23: Add DMA driver
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
` (2 preceding siblings ...)
2013-12-11 13:56 ` [Qemu-devel] [PATCH 03/13] mxs/imx23: Add uart driver Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2014-01-06 15:35 ` Peter Maydell
2013-12-11 13:56 ` [Qemu-devel] [PATCH 05/13] mxs/imx23: Add the interrupt collector Michel Pollet
` (9 subsequent siblings)
13 siblings, 1 reply; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
This driver works sufficiently well that linux can use it to access
the SD card using the SD->DMA->SSI->SD. It hasn't been tested for
much else.
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/dma/Makefile.objs | 1 +
hw/dma/mxs_dma.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 348 insertions(+)
create mode 100644 hw/dma/mxs_dma.c
diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
index 0e65ed0..3373aa1 100644
--- a/hw/dma/Makefile.objs
+++ b/hw/dma/Makefile.objs
@@ -8,6 +8,7 @@ common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o
common-obj-$(CONFIG_STP2000) += sparc32_dma.o
common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
+common-obj-$(CONFIG_MXS) += mxs_dma.o
obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
diff --git a/hw/dma/mxs_dma.c b/hw/dma/mxs_dma.c
new file mode 100644
index 0000000..9ac787b
--- /dev/null
+++ b/hw/dma/mxs_dma.c
@@ -0,0 +1,347 @@
+/*
+ * mxs_dma.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ */
+
+/*
+ * Implements the DMA block of the mxs.
+ * The current implementation can run chains of commands etc, however it's only
+ * been tested with SSP for SD/MMC card access. It ought to work with normal SPI
+ * too, and possibly other peripherals, however it's entirely untested
+ */
+#include "hw/sysbus.h"
+#include "hw/arm/mxs.h"
+
+/*
+ * DMA IO block register numbers
+ */
+enum {
+ DMA_CTRL0 = 0x0,
+ DMA_CTRL1 = 0x1,
+ DMA_CTRL2 = 0x2,
+ DMA_DEVSEL1 = 0x3,
+ DMA_DEVSEL2 = 0x4,
+ DMA_MAX,
+
+ /*
+ * The DMA block for APBH and APBX have a different base address,
+ * but they share a 7 words stride between channels.
+ */
+ DMA_STRIDE = 0x70,
+ /*
+ * Neither blocks uses that many, but there is space for them...
+ */
+ DMA_MAX_CHANNELS = 16,
+};
+
+/*
+ * DMA channel register numbers
+ */
+enum {
+ CH_CURCMD = 0,
+ CH_NEXTCMD = 1,
+ CH_CMD = 2,
+ CH_BUFFER_ADDR = 3,
+ CH_SEMA = 4,
+ CH_DEBUG1 = 5,
+ CH_DEBUG2 = 6,
+};
+
+/*
+ * Channel command bit numbers
+ */
+enum {
+ CH_CMD_IRQ_COMPLETE = 3,
+ CH_CMD_SEMAPHORE = 6,
+};
+
+/*
+ * nicked from linux
+ * this is the memory representation of a DMA request
+ */
+struct mxs_dma_ccw {
+ uint32_t next;
+ uint16_t bits;
+ uint16_t xfer_bytes;
+#define MAX_XFER_BYTES 0xff00
+ uint32_t bufaddr;
+#define MXS_PIO_WORDS 16
+ uint32_t pio_words[MXS_PIO_WORDS];
+}__attribute__((packed));
+
+/*
+ * Per channel DMA description
+ */
+typedef struct mxs_dma_channel {
+ QEMUTimer *timer;
+ struct mxs_dma_state *dma;
+ int channel; // channel index
+ hwaddr base; // base of peripheral
+ hwaddr dataoffset; // offset of the true in/out data latch register
+ uint32_t r[10];
+ qemu_irq irq;
+} mxs_dma_channel;
+
+
+typedef struct mxs_dma_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ const char * name;
+
+ struct soc_dma_s * dma;
+ uint32_t r[DMA_MAX];
+
+ hwaddr base; // base of peripheral
+ mxs_dma_channel channel[DMA_MAX_CHANNELS];
+} mxs_dma_state;
+
+static void mxs_dma_ch_update(mxs_dma_channel *s)
+{
+ struct mxs_dma_ccw req;
+ int i;
+
+ /* increment the semaphore, if needed */
+ s->r[CH_SEMA] = (((s->r[CH_SEMA] >> 16) & 0xff) +
+ (s->r[CH_SEMA] & 0xff)) << 16;
+ if (!((s->r[CH_SEMA] >> 16) & 0xff)) {
+ return;
+ }
+ /* read the request from memory */
+ cpu_physical_memory_read(s->r[CH_NEXTCMD], &req, sizeof(req));
+ /* update the latch registers accordingly */
+ s->r[CH_CURCMD] = s->r[CH_NEXTCMD];
+ s->r[CH_NEXTCMD] = req.next;
+ s->r[CH_CMD] = (req.xfer_bytes << 16) | req.bits;
+ s->r[CH_BUFFER_ADDR] = req.bufaddr;
+
+ /* write PIO registers first, if any */
+ for (i = 0; i < (req.bits >> 12); i++) {
+ cpu_physical_memory_rw(s->base + (i << 4),
+ (uint8_t*) &req.pio_words[i], 4, 1);
+ }
+ /* next handle any "data" requests */
+ switch (req.bits & 0x3) {
+ case 0:
+ break; // PIO only
+ case 0x1: { // WRITE (from periph to memory)
+ uint32_t buf = req.bufaddr;
+ uint8_t b = 0;
+ while (req.xfer_bytes--) {
+ cpu_physical_memory_rw(s->base + s->dataoffset, &b, 1, 0);
+ cpu_physical_memory_rw(buf, &b, 1, 1);
+ buf++;
+ }
+ } break;
+ case 0x2: { // READ (from memory to periph)
+ uint32_t buf = req.bufaddr;
+ uint8_t b = 0;
+ while (req.xfer_bytes--) {
+ cpu_physical_memory_rw(buf, &b, 1, 0);
+ cpu_physical_memory_rw(s->base + s->dataoffset, &b, 1, 1);
+ buf++;
+ }
+ } break;
+ }
+
+ s->dma->r[DMA_CTRL1] |= 1 << s->channel;
+ /* trigger IRQ if requested */
+ if ((s->dma->r[DMA_CTRL1] >> 16) & (1 << s->channel)) {
+ if (req.bits & (1 << CH_CMD_IRQ_COMPLETE)) {
+ qemu_set_irq(s->irq, 1);
+ }
+ }
+
+ /* decrement semaphore if requested */
+ if (s->r[CH_CMD] & (1 << CH_CMD_SEMAPHORE)) {
+ s->r[CH_SEMA] = (((s->r[CH_SEMA] >> 16) & 0xff) - 1) << 16;
+ }
+ /* If the semaphore is still on, try to trigger a chained request */
+ if ((s->r[CH_SEMA] >> 16) & 0xff) {
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ timer_mod(s->timer, now + 10);
+ }
+}
+
+/* called on one shot timer activation */
+static void mxs_dma_ch_run(void *opaque)
+{
+ mxs_dma_channel *s = opaque;
+ mxs_dma_ch_update(s);
+}
+
+static uint64_t mxs_dma_read(void *opaque, hwaddr offset, unsigned size)
+{
+ mxs_dma_state *s = (mxs_dma_state *) opaque;
+ uint32_t res = 0;
+
+ switch (offset >> 4) {
+ case 0 ... DMA_MAX - 1:
+ res = s->r[offset >> 4];
+ break;
+ default:
+ if (offset >= s->base) {
+ offset -= s->base;
+ int channel = offset / DMA_STRIDE;
+ int word = (offset % DMA_STRIDE) >> 4;
+ res = s->channel[channel].r[word];
+ } else
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+
+ return res;
+}
+
+static void mxs_dma_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ mxs_dma_state *s = (mxs_dma_state *) opaque;
+ uint32_t oldvalue = 0;
+ int channel, word, i;
+
+ switch (offset >> 4) {
+ case 0 ... DMA_MAX - 1:
+ oldvalue = mxs_write(&s->r[offset >> 4], offset, value, size);
+ break;
+ default:
+ if (offset >= s->base) {
+ channel = (offset - s->base) / DMA_STRIDE;
+ word = (offset - s->base) % DMA_STRIDE;
+ oldvalue = mxs_write(
+ &s->channel[channel].r[word >> 4], word,
+ value, size);
+ switch (word >> 4) {
+ case CH_SEMA:
+ // mask the new semaphore value, as only the lowest 8 bits are RW
+ s->channel[channel].r[CH_SEMA] =
+ (oldvalue & ~0xff) |
+ (s->channel[channel].r[CH_SEMA] & 0xff);
+ mxs_dma_ch_update(&s->channel[channel]);
+ break;
+ }
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ }
+ break;
+ }
+ switch (offset >> 4) {
+ case DMA_CTRL0:
+ if ((oldvalue ^ s->r[DMA_CTRL0]) == 0x80000000
+ && !(oldvalue & 0x80000000)) {
+ // printf("%s write reseting, anding clockgate\n", s->name);
+ s->r[DMA_CTRL0] |= 0x40000000;
+ }
+ break;
+ case DMA_CTRL1:
+ for (i = 0; i < DMA_MAX_CHANNELS; i++)
+ if (s->channel[i].r[CH_NEXTCMD] &&
+ !(s->r[DMA_CTRL1] & (1 << i))) {
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ /* add a bit of latency to the timer. Ideally would
+ * do some calculation proportional to the transfer
+ * size. TODO ?
+ */
+ timer_mod(s->channel[i].timer, now + 100000);
+ }
+ break;
+ }
+}
+
+
+static const MemoryRegionOps mxs_dma_ops = {
+ .read = mxs_dma_read,
+ .write = mxs_dma_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void mxs_dma_common_init(mxs_dma_state *s)
+{
+ int i;
+ memory_region_init_io(&s->iomem, OBJECT(s), &mxs_dma_ops, s, "mxs_dma", 0x2000);
+ sysbus_init_mmio(&s->busdev, &s->iomem);
+ for (i = 0; i < DMA_MAX_CHANNELS; i++) {
+ s->channel[i].dma = s;
+ s->channel[i].channel = i;
+ s->channel[i].timer =
+ timer_new_ns(QEMU_CLOCK_VIRTUAL, mxs_dma_ch_run, &s->channel[i]);
+ }
+}
+
+static int mxs_apbh_dma_init(SysBusDevice *dev)
+{
+ mxs_dma_state *s = OBJECT_CHECK(mxs_dma_state, dev, "mxs_apbh_dma");
+
+ mxs_dma_common_init(s);
+ s->name = "dma_apbh";
+ s->base = 0x40;
+ sysbus_init_irq(dev, &s->channel[MX23_DMA_SSP1].irq);
+ sysbus_init_irq(dev, &s->channel[MX23_DMA_SSP2].irq);
+ s->channel[MX23_DMA_SSP1].base = MX23_SSP1_BASE_ADDR;
+ s->channel[MX23_DMA_SSP1].dataoffset = 0x70;
+ s->channel[MX23_DMA_SSP2].base = MX23_SSP2_BASE_ADDR;
+ s->channel[MX23_DMA_SSP2].dataoffset = 0x70;
+
+ return 0;
+}
+
+static int mxs_apbx_dma_init(SysBusDevice *dev)
+{
+// mxs_dma_state *s = FROM_SYSBUS(mxs_dma_state, dev);
+ mxs_dma_state *s = OBJECT_CHECK(mxs_dma_state, dev, "mxs_apbx_dma");
+
+ mxs_dma_common_init(s);
+ s->name = "dma_apbx";
+ s->base = 0x100;
+ sysbus_init_irq(dev, &s->channel[MX23_DMA_ADC].irq);
+ sysbus_init_irq(dev, &s->channel[MX23_DMA_DAC].irq);
+ sysbus_init_irq(dev, &s->channel[MX23_DMA_SPDIF].irq);
+ sysbus_init_irq(dev, &s->channel[MX23_DMA_I2C].irq);
+ sysbus_init_irq(dev, &s->channel[MX23_DMA_SAIF0].irq);
+ sysbus_init_irq(dev, &s->channel[MX23_DMA_UART0_RX].irq);
+ sysbus_init_irq(dev, &s->channel[MX23_DMA_UART0_TX].irq);
+ sysbus_init_irq(dev, &s->channel[MX23_DMA_UART1_RX].irq);
+ sysbus_init_irq(dev, &s->channel[MX23_DMA_UART1_TX].irq);
+ sysbus_init_irq(dev, &s->channel[MX23_DMA_SAIF1].irq);
+
+ return 0;
+}
+
+static void mxs_apbh_dma_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = mxs_apbh_dma_init;
+}
+
+static void mxs_apbx_dma_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = mxs_apbx_dma_init;
+}
+
+static TypeInfo apbh_dma_info = {
+ .name = "mxs_apbh_dma",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mxs_dma_state),
+ .class_init = mxs_apbh_dma_class_init,
+};
+static TypeInfo apbx_dma_info = {
+ .name = "mxs_apbx_dma",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mxs_dma_state),
+ .class_init = mxs_apbx_dma_class_init,
+};
+
+static void mxs_dma_register(void)
+{
+ type_register_static(&apbh_dma_info);
+ type_register_static(&apbx_dma_info);
+}
+
+type_init(mxs_dma_register)
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 04/13] mxs/imx23: Add DMA driver
2013-12-11 13:56 ` [Qemu-devel] [PATCH 04/13] mxs/imx23: Add DMA driver Michel Pollet
@ 2014-01-06 15:35 ` Peter Maydell
2014-01-10 0:52 ` Peter Crosthwaite
0 siblings, 1 reply; 36+ messages in thread
From: Peter Maydell @ 2014-01-06 15:35 UTC (permalink / raw)
To: Michel Pollet; +Cc: Peter Crosthwaite, QEMU Developers
On 11 December 2013 13:56, Michel Pollet <buserror@gmail.com> wrote:
> This driver works sufficiently well that linux can use it to access
> the SD card using the SD->DMA->SSI->SD. It hasn't been tested for
> much else.
>
> Signed-off-by: Michel Pollet <buserror@gmail.com>
> ---
> hw/dma/Makefile.objs | 1 +
> hw/dma/mxs_dma.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 348 insertions(+)
> create mode 100644 hw/dma/mxs_dma.c
>
> diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
> index 0e65ed0..3373aa1 100644
> --- a/hw/dma/Makefile.objs
> +++ b/hw/dma/Makefile.objs
> @@ -8,6 +8,7 @@ common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
> common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o
> common-obj-$(CONFIG_STP2000) += sparc32_dma.o
> common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
> +common-obj-$(CONFIG_MXS) += mxs_dma.o
>
> obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
> obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
> diff --git a/hw/dma/mxs_dma.c b/hw/dma/mxs_dma.c
> new file mode 100644
> index 0000000..9ac787b
> --- /dev/null
> +++ b/hw/dma/mxs_dma.c
> @@ -0,0 +1,347 @@
> +/*
> + * mxs_dma.c
> + *
> + * Copyright: Michel Pollet <buserror@gmail.com>
> + *
> + * QEMU Licence
> + */
> +
> +/*
> + * Implements the DMA block of the mxs.
> + * The current implementation can run chains of commands etc, however it's only
> + * been tested with SSP for SD/MMC card access. It ought to work with normal SPI
> + * too, and possibly other peripherals, however it's entirely untested
> + */
> +#include "hw/sysbus.h"
> +#include "hw/arm/mxs.h"
> +
> +/*
> + * DMA IO block register numbers
> + */
> +enum {
> + DMA_CTRL0 = 0x0,
> + DMA_CTRL1 = 0x1,
> + DMA_CTRL2 = 0x2,
> + DMA_DEVSEL1 = 0x3,
> + DMA_DEVSEL2 = 0x4,
> + DMA_MAX,
> +
> + /*
> + * The DMA block for APBH and APBX have a different base address,
> + * but they share a 7 words stride between channels.
> + */
> + DMA_STRIDE = 0x70,
> + /*
> + * Neither blocks uses that many, but there is space for them...
> + */
> + DMA_MAX_CHANNELS = 16,
> +};
> +
> +/*
> + * DMA channel register numbers
> + */
> +enum {
> + CH_CURCMD = 0,
> + CH_NEXTCMD = 1,
> + CH_CMD = 2,
> + CH_BUFFER_ADDR = 3,
> + CH_SEMA = 4,
> + CH_DEBUG1 = 5,
> + CH_DEBUG2 = 6,
> +};
> +
> +/*
> + * Channel command bit numbers
> + */
> +enum {
> + CH_CMD_IRQ_COMPLETE = 3,
> + CH_CMD_SEMAPHORE = 6,
> +};
> +
> +/*
> + * nicked from linux
You don't need to say that, it doesn't really add any
information to the reader.
> + * this is the memory representation of a DMA request
> + */
> +struct mxs_dma_ccw {
> + uint32_t next;
> + uint16_t bits;
> + uint16_t xfer_bytes;
> +#define MAX_XFER_BYTES 0xff00
I'd rather have the #defines before the struct than
interleaved, personally.
> + uint32_t bufaddr;
> +#define MXS_PIO_WORDS 16
> + uint32_t pio_words[MXS_PIO_WORDS];
> +}__attribute__((packed));
If you need to use packed then always use the QEMU_PACKED
macro; this ensures the same layout on all host platforms
(Windows behaviour of plain attribute packed is different).
> +
> +/*
> + * Per channel DMA description
> + */
> +typedef struct mxs_dma_channel {
> + QEMUTimer *timer;
> + struct mxs_dma_state *dma;
> + int channel; // channel index
> + hwaddr base; // base of peripheral
> + hwaddr dataoffset; // offset of the true in/out data latch register
No C++-style comments, please.
> + uint32_t r[10];
> + qemu_irq irq;
> +} mxs_dma_channel;
> +
> +
> +typedef struct mxs_dma_state {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> + const char * name;
> +
> + struct soc_dma_s * dma;
> + uint32_t r[DMA_MAX];
> +
> + hwaddr base; // base of peripheral
> + mxs_dma_channel channel[DMA_MAX_CHANNELS];
> +} mxs_dma_state;
> +
> +static void mxs_dma_ch_update(mxs_dma_channel *s)
> +{
> + struct mxs_dma_ccw req;
> + int i;
> +
> + /* increment the semaphore, if needed */
> + s->r[CH_SEMA] = (((s->r[CH_SEMA] >> 16) & 0xff) +
> + (s->r[CH_SEMA] & 0xff)) << 16;
This is confusing -- what's it actually doing?
> + if (!((s->r[CH_SEMA] >> 16) & 0xff)) {
> + return;
> + }
> + /* read the request from memory */
> + cpu_physical_memory_read(s->r[CH_NEXTCMD], &req, sizeof(req));
This will not work if your host and target have opposite
endianness. If you're going to read the whole struct in
at once like this you need to use the cpu_to_le* functions
to swap all its members to the host endianness.
Similarly anywhere you write a block of memory back.
Alternatively you can use the ld*_le_phys and st*_le_phys
to read and write specifically sized bytes/shorts/words
to little-endian guest order.
> + /* update the latch registers accordingly */
> + s->r[CH_CURCMD] = s->r[CH_NEXTCMD];
> + s->r[CH_NEXTCMD] = req.next;
> + s->r[CH_CMD] = (req.xfer_bytes << 16) | req.bits;
> + s->r[CH_BUFFER_ADDR] = req.bufaddr;
> +
> + /* write PIO registers first, if any */
> + for (i = 0; i < (req.bits >> 12); i++) {
> + cpu_physical_memory_rw(s->base + (i << 4),
> + (uint8_t*) &req.pio_words[i], 4, 1);
...for instance this is probably best done via
stl_le_phys() to write each word to the guest memory.
> + }
> + /* next handle any "data" requests */
> + switch (req.bits & 0x3) {
> + case 0:
> + break; // PIO only
> + case 0x1: { // WRITE (from periph to memory)
> + uint32_t buf = req.bufaddr;
> + uint8_t b = 0;
> + while (req.xfer_bytes--) {
> + cpu_physical_memory_rw(s->base + s->dataoffset, &b, 1, 0);
> + cpu_physical_memory_rw(buf, &b, 1, 1);
> + buf++;
> + }
> + } break;
> + case 0x2: { // READ (from memory to periph)
> + uint32_t buf = req.bufaddr;
> + uint8_t b = 0;
> + while (req.xfer_bytes--) {
> + cpu_physical_memory_rw(buf, &b, 1, 0);
> + cpu_physical_memory_rw(s->base + s->dataoffset, &b, 1, 1);
> + buf++;
> + }
> + } break;
> + }
> +
> + s->dma->r[DMA_CTRL1] |= 1 << s->channel;
> + /* trigger IRQ if requested */
> + if ((s->dma->r[DMA_CTRL1] >> 16) & (1 << s->channel)) {
> + if (req.bits & (1 << CH_CMD_IRQ_COMPLETE)) {
> + qemu_set_irq(s->irq, 1);
> + }
> + }
> +
> + /* decrement semaphore if requested */
> + if (s->r[CH_CMD] & (1 << CH_CMD_SEMAPHORE)) {
> + s->r[CH_SEMA] = (((s->r[CH_SEMA] >> 16) & 0xff) - 1) << 16;
> + }
> + /* If the semaphore is still on, try to trigger a chained request */
> + if ((s->r[CH_SEMA] >> 16) & 0xff) {
> + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> + timer_mod(s->timer, now + 10);
This stuff involving the timer looks a bit fishy, but I'm
not really up on current best practice with DMA, timers, etc.
Peter Crosthwaite may have a more informed opinion.
> + }
> +}
> +
> +/* called on one shot timer activation */
> +static void mxs_dma_ch_run(void *opaque)
> +{
> + mxs_dma_channel *s = opaque;
> + mxs_dma_ch_update(s);
> +}
> +
> +static uint64_t mxs_dma_read(void *opaque, hwaddr offset, unsigned size)
> +{
> + mxs_dma_state *s = (mxs_dma_state *) opaque;
> + uint32_t res = 0;
> +
> + switch (offset >> 4) {
> + case 0 ... DMA_MAX - 1:
> + res = s->r[offset >> 4];
> + break;
> + default:
> + if (offset >= s->base) {
> + offset -= s->base;
> + int channel = offset / DMA_STRIDE;
> + int word = (offset % DMA_STRIDE) >> 4;
> + res = s->channel[channel].r[word];
> + } else
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> +
> + return res;
> +}
> +
> +static void mxs_dma_write(void *opaque, hwaddr offset, uint64_t value,
> + unsigned size)
> +{
> + mxs_dma_state *s = (mxs_dma_state *) opaque;
> + uint32_t oldvalue = 0;
> + int channel, word, i;
> +
> + switch (offset >> 4) {
> + case 0 ... DMA_MAX - 1:
> + oldvalue = mxs_write(&s->r[offset >> 4], offset, value, size);
> + break;
> + default:
> + if (offset >= s->base) {
> + channel = (offset - s->base) / DMA_STRIDE;
> + word = (offset - s->base) % DMA_STRIDE;
> + oldvalue = mxs_write(
> + &s->channel[channel].r[word >> 4], word,
> + value, size);
> + switch (word >> 4) {
> + case CH_SEMA:
> + // mask the new semaphore value, as only the lowest 8 bits are RW
> + s->channel[channel].r[CH_SEMA] =
> + (oldvalue & ~0xff) |
> + (s->channel[channel].r[CH_SEMA] & 0xff);
You can do this with
s->channel[channel].r[CH_SEMA] = deposit32(oldvalue, 0, 8,
s->channel[channel].r[CH_SEMA]);
> + mxs_dma_ch_update(&s->channel[channel]);
> + break;
> + }
> + } else {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + }
> + break;
> + }
> + switch (offset >> 4) {
> + case DMA_CTRL0:
> + if ((oldvalue ^ s->r[DMA_CTRL0]) == 0x80000000
> + && !(oldvalue & 0x80000000)) {
> + // printf("%s write reseting, anding clockgate\n", s->name);
> + s->r[DMA_CTRL0] |= 0x40000000;
> + }
> + break;
> + case DMA_CTRL1:
> + for (i = 0; i < DMA_MAX_CHANNELS; i++)
> + if (s->channel[i].r[CH_NEXTCMD] &&
> + !(s->r[DMA_CTRL1] & (1 << i))) {
> + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> + /* add a bit of latency to the timer. Ideally would
> + * do some calculation proportional to the transfer
> + * size. TODO ?
> + */
> + timer_mod(s->channel[i].timer, now + 100000);
> + }
> + break;
> + }
> +}
> +
> +
> +static const MemoryRegionOps mxs_dma_ops = {
> + .read = mxs_dma_read,
> + .write = mxs_dma_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static void mxs_dma_common_init(mxs_dma_state *s)
> +{
> + int i;
> + memory_region_init_io(&s->iomem, OBJECT(s), &mxs_dma_ops, s, "mxs_dma", 0x2000);
> + sysbus_init_mmio(&s->busdev, &s->iomem);
> + for (i = 0; i < DMA_MAX_CHANNELS; i++) {
> + s->channel[i].dma = s;
> + s->channel[i].channel = i;
> + s->channel[i].timer =
> + timer_new_ns(QEMU_CLOCK_VIRTUAL, mxs_dma_ch_run, &s->channel[i]);
> + }
> +}
> +
> +static int mxs_apbh_dma_init(SysBusDevice *dev)
> +{
> + mxs_dma_state *s = OBJECT_CHECK(mxs_dma_state, dev, "mxs_apbh_dma");
> +
> + mxs_dma_common_init(s);
> + s->name = "dma_apbh";
> + s->base = 0x40;
> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SSP1].irq);
> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SSP2].irq);
> + s->channel[MX23_DMA_SSP1].base = MX23_SSP1_BASE_ADDR;
> + s->channel[MX23_DMA_SSP1].dataoffset = 0x70;
> + s->channel[MX23_DMA_SSP2].base = MX23_SSP2_BASE_ADDR;
> + s->channel[MX23_DMA_SSP2].dataoffset = 0x70;
> +
> + return 0;
> +}
> +
> +static int mxs_apbx_dma_init(SysBusDevice *dev)
> +{
> +// mxs_dma_state *s = FROM_SYSBUS(mxs_dma_state, dev);
> + mxs_dma_state *s = OBJECT_CHECK(mxs_dma_state, dev, "mxs_apbx_dma");
> +
> + mxs_dma_common_init(s);
> + s->name = "dma_apbx";
> + s->base = 0x100;
> + sysbus_init_irq(dev, &s->channel[MX23_DMA_ADC].irq);
> + sysbus_init_irq(dev, &s->channel[MX23_DMA_DAC].irq);
> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SPDIF].irq);
> + sysbus_init_irq(dev, &s->channel[MX23_DMA_I2C].irq);
> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SAIF0].irq);
> + sysbus_init_irq(dev, &s->channel[MX23_DMA_UART0_RX].irq);
> + sysbus_init_irq(dev, &s->channel[MX23_DMA_UART0_TX].irq);
> + sysbus_init_irq(dev, &s->channel[MX23_DMA_UART1_RX].irq);
> + sysbus_init_irq(dev, &s->channel[MX23_DMA_UART1_TX].irq);
> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SAIF1].irq);
> +
> + return 0;
> +}
> +
> +static void mxs_apbh_dma_class_init(ObjectClass *klass, void *data)
> +{
> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +
> + sdc->init = mxs_apbh_dma_init;
> +}
> +
> +static void mxs_apbx_dma_class_init(ObjectClass *klass, void *data)
> +{
> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +
> + sdc->init = mxs_apbx_dma_init;
> +}
Needs reset and vmstate.
> +
> +static TypeInfo apbh_dma_info = {
> + .name = "mxs_apbh_dma",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(mxs_dma_state),
> + .class_init = mxs_apbh_dma_class_init,
> +};
> +static TypeInfo apbx_dma_info = {
> + .name = "mxs_apbx_dma",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(mxs_dma_state),
> + .class_init = mxs_apbx_dma_class_init,
> +};
> +
> +static void mxs_dma_register(void)
> +{
> + type_register_static(&apbh_dma_info);
> + type_register_static(&apbx_dma_info);
> +}
> +
> +type_init(mxs_dma_register)
> --
> 1.8.5.1
thanks
-- PMM
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 04/13] mxs/imx23: Add DMA driver
2014-01-06 15:35 ` Peter Maydell
@ 2014-01-10 0:52 ` Peter Crosthwaite
2014-01-10 0:54 ` Peter Crosthwaite
2014-01-10 10:55 ` Peter Maydell
0 siblings, 2 replies; 36+ messages in thread
From: Peter Crosthwaite @ 2014-01-10 0:52 UTC (permalink / raw)
To: Peter Maydell; +Cc: Michel Pollet, QEMU Developers
On Tue, Jan 7, 2014 at 1:35 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 11 December 2013 13:56, Michel Pollet <buserror@gmail.com> wrote:
>> This driver works sufficiently well that linux can use it to access
>> the SD card using the SD->DMA->SSI->SD. It hasn't been tested for
>> much else.
>>
>> Signed-off-by: Michel Pollet <buserror@gmail.com>
>> ---
>> hw/dma/Makefile.objs | 1 +
>> hw/dma/mxs_dma.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 348 insertions(+)
>> create mode 100644 hw/dma/mxs_dma.c
>>
>> diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
>> index 0e65ed0..3373aa1 100644
>> --- a/hw/dma/Makefile.objs
>> +++ b/hw/dma/Makefile.objs
>> @@ -8,6 +8,7 @@ common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
>> common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o
>> common-obj-$(CONFIG_STP2000) += sparc32_dma.o
>> common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
>> +common-obj-$(CONFIG_MXS) += mxs_dma.o
>>
>> obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
>> obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
>> diff --git a/hw/dma/mxs_dma.c b/hw/dma/mxs_dma.c
>> new file mode 100644
>> index 0000000..9ac787b
>> --- /dev/null
>> +++ b/hw/dma/mxs_dma.c
>> @@ -0,0 +1,347 @@
>> +/*
>> + * mxs_dma.c
>> + *
>> + * Copyright: Michel Pollet <buserror@gmail.com>
>> + *
>> + * QEMU Licence
>> + */
>> +
>> +/*
>> + * Implements the DMA block of the mxs.
>> + * The current implementation can run chains of commands etc, however it's only
>> + * been tested with SSP for SD/MMC card access. It ought to work with normal SPI
>> + * too, and possibly other peripherals, however it's entirely untested
>> + */
>> +#include "hw/sysbus.h"
>> +#include "hw/arm/mxs.h"
>> +
>> +/*
>> + * DMA IO block register numbers
>> + */
>> +enum {
>> + DMA_CTRL0 = 0x0,
>> + DMA_CTRL1 = 0x1,
>> + DMA_CTRL2 = 0x2,
>> + DMA_DEVSEL1 = 0x3,
>> + DMA_DEVSEL2 = 0x4,
>> + DMA_MAX,
>> +
>> + /*
>> + * The DMA block for APBH and APBX have a different base address,
>> + * but they share a 7 words stride between channels.
>> + */
>> + DMA_STRIDE = 0x70,
>> + /*
>> + * Neither blocks uses that many, but there is space for them...
>> + */
>> + DMA_MAX_CHANNELS = 16,
>> +};
>> +
>> +/*
>> + * DMA channel register numbers
>> + */
>> +enum {
>> + CH_CURCMD = 0,
>> + CH_NEXTCMD = 1,
>> + CH_CMD = 2,
>> + CH_BUFFER_ADDR = 3,
>> + CH_SEMA = 4,
>> + CH_DEBUG1 = 5,
>> + CH_DEBUG2 = 6,
>> +};
>> +
>> +/*
>> + * Channel command bit numbers
>> + */
>> +enum {
>> + CH_CMD_IRQ_COMPLETE = 3,
>> + CH_CMD_SEMAPHORE = 6,
>> +};
>> +
>> +/*
>> + * nicked from linux
>
> You don't need to say that, it doesn't really add any
> information to the reader.
>
>> + * this is the memory representation of a DMA request
>> + */
>> +struct mxs_dma_ccw {
>> + uint32_t next;
>> + uint16_t bits;
>> + uint16_t xfer_bytes;
>> +#define MAX_XFER_BYTES 0xff00
>
> I'd rather have the #defines before the struct than
> interleaved, personally.
>
TBH, this is the same as my own preferred personal coding style (and I
refactor on upstreaming due to it's contention). I'd like to push for
it's general acceptance. #defining at the point of relevance is a well
adopted concept and makes code much more readable.
>> + uint32_t bufaddr;
>> +#define MXS_PIO_WORDS 16
>> + uint32_t pio_words[MXS_PIO_WORDS];
>> +}__attribute__((packed));
>
> If you need to use packed then always use the QEMU_PACKED
> macro; this ensures the same layout on all host platforms
> (Windows behaviour of plain attribute packed is different).
>
>
>> +
>> +/*
>> + * Per channel DMA description
>> + */
>> +typedef struct mxs_dma_channel {
>> + QEMUTimer *timer;
>> + struct mxs_dma_state *dma;
>> + int channel; // channel index
>> + hwaddr base; // base of peripheral
>> + hwaddr dataoffset; // offset of the true in/out data latch register
>
> No C++-style comments, please.
>
>> + uint32_t r[10];
>> + qemu_irq irq;
>> +} mxs_dma_channel;
>> +
>> +
>> +typedef struct mxs_dma_state {
>> + SysBusDevice busdev;
parent_obj. Check your QOM style.
http://lists.gnu.org/archive/html/qemu-devel/2013-02/msg05045.html
>> + MemoryRegion iomem;
>> + const char * name;
>> +
>> + struct soc_dma_s * dma;
>> + uint32_t r[DMA_MAX];
>> +
>> + hwaddr base; // base of peripheral
>> + mxs_dma_channel channel[DMA_MAX_CHANNELS];
>> +} mxs_dma_state;
>> +
>> +static void mxs_dma_ch_update(mxs_dma_channel *s)
>> +{
>> + struct mxs_dma_ccw req;
>> + int i;
>> +
>> + /* increment the semaphore, if needed */
>> + s->r[CH_SEMA] = (((s->r[CH_SEMA] >> 16) & 0xff) +
>> + (s->r[CH_SEMA] & 0xff)) << 16;
>
> This is confusing -- what's it actually doing?
>
>> + if (!((s->r[CH_SEMA] >> 16) & 0xff)) {
>> + return;
>> + }
>> + /* read the request from memory */
>> + cpu_physical_memory_read(s->r[CH_NEXTCMD], &req, sizeof(req));
>
> This will not work if your host and target have opposite
> endianness. If you're going to read the whole struct in
> at once like this you need to use the cpu_to_le* functions
> to swap all its members to the host endianness.
> Similarly anywhere you write a block of memory back.
> Alternatively you can use the ld*_le_phys and st*_le_phys
> to read and write specifically sized bytes/shorts/words
> to little-endian guest order.
>
>> + /* update the latch registers accordingly */
>> + s->r[CH_CURCMD] = s->r[CH_NEXTCMD];
>> + s->r[CH_NEXTCMD] = req.next;
>> + s->r[CH_CMD] = (req.xfer_bytes << 16) | req.bits;
>> + s->r[CH_BUFFER_ADDR] = req.bufaddr;
>> +
>> + /* write PIO registers first, if any */
>> + for (i = 0; i < (req.bits >> 12); i++) {
>> + cpu_physical_memory_rw(s->base + (i << 4),
>> + (uint8_t*) &req.pio_words[i], 4, 1);
>
> ...for instance this is probably best done via
> stl_le_phys() to write each word to the guest memory.
>
Last I knew, dma_memory_read|write is the correct way to do DMA from
device land. cpu_physical_memory and stl_ are more CPU concepts.
dma_memory_read|write has the added advantage of accepting and
address_space which allows for DMA in machines with non-flat
bus/memory layouts.
>> + }
>> + /* next handle any "data" requests */
>> + switch (req.bits & 0x3) {
>> + case 0:
>> + break; // PIO only
>> + case 0x1: { // WRITE (from periph to memory)
>> + uint32_t buf = req.bufaddr;
>> + uint8_t b = 0;
>> + while (req.xfer_bytes--) {
>> + cpu_physical_memory_rw(s->base + s->dataoffset, &b, 1, 0);
>> + cpu_physical_memory_rw(buf, &b, 1, 1);
>> + buf++;
>> + }
>> + } break;
>> + case 0x2: { // READ (from memory to periph)
>> + uint32_t buf = req.bufaddr;
>> + uint8_t b = 0;
>> + while (req.xfer_bytes--) {
>> + cpu_physical_memory_rw(buf, &b, 1, 0);
>> + cpu_physical_memory_rw(s->base + s->dataoffset, &b, 1, 1);
>> + buf++;
>> + }
>> + } break;
>> + }
>> +
>> + s->dma->r[DMA_CTRL1] |= 1 << s->channel;
>> + /* trigger IRQ if requested */
>> + if ((s->dma->r[DMA_CTRL1] >> 16) & (1 << s->channel)) {
>> + if (req.bits & (1 << CH_CMD_IRQ_COMPLETE)) {
>> + qemu_set_irq(s->irq, 1);
>> + }
>> + }
>> +
>> + /* decrement semaphore if requested */
>> + if (s->r[CH_CMD] & (1 << CH_CMD_SEMAPHORE)) {
>> + s->r[CH_SEMA] = (((s->r[CH_SEMA] >> 16) & 0xff) - 1) << 16;
>> + }
>> + /* If the semaphore is still on, try to trigger a chained request */
>> + if ((s->r[CH_SEMA] >> 16) & 0xff) {
>> + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>> + timer_mod(s->timer, now + 10);
>
> This stuff involving the timer looks a bit fishy, but I'm
> not really up on current best practice with DMA, timers, etc.
> Peter Crosthwaite may have a more informed opinion.
>
I'll take a step back and do a full series review. As of this mail all
i've done is a quick style scan, so ill go more in-depth on either
this V2 or this if Michel respins. Give me a few days. Don't wait for
me, I'd prefer to look at v2 as PMM has already asked a good number of
changes. cc me for a speedy review.
>> + }
>> +}
>> +
>> +/* called on one shot timer activation */
>> +static void mxs_dma_ch_run(void *opaque)
>> +{
>> + mxs_dma_channel *s = opaque;
>> + mxs_dma_ch_update(s);
>> +}
>> +
>> +static uint64_t mxs_dma_read(void *opaque, hwaddr offset, unsigned size)
>> +{
>> + mxs_dma_state *s = (mxs_dma_state *) opaque;
>> + uint32_t res = 0;
>> +
>> + switch (offset >> 4) {
>> + case 0 ... DMA_MAX - 1:
>> + res = s->r[offset >> 4];
>> + break;
>> + default:
>> + if (offset >= s->base) {
>> + offset -= s->base;
>> + int channel = offset / DMA_STRIDE;
>> + int word = (offset % DMA_STRIDE) >> 4;
>> + res = s->channel[channel].r[word];
>> + } else
>> + qemu_log_mask(LOG_GUEST_ERROR,
>> + "%s: bad offset 0x%x\n", __func__, (int) offset);
>> + break;
>> + }
>> +
>> + return res;
>> +}
>> +
>> +static void mxs_dma_write(void *opaque, hwaddr offset, uint64_t value,
>> + unsigned size)
>> +{
>> + mxs_dma_state *s = (mxs_dma_state *) opaque;
>> + uint32_t oldvalue = 0;
>> + int channel, word, i;
>> +
>> + switch (offset >> 4) {
>> + case 0 ... DMA_MAX - 1:
>> + oldvalue = mxs_write(&s->r[offset >> 4], offset, value, size);
>> + break;
>> + default:
>> + if (offset >= s->base) {
>> + channel = (offset - s->base) / DMA_STRIDE;
>> + word = (offset - s->base) % DMA_STRIDE;
>> + oldvalue = mxs_write(
>> + &s->channel[channel].r[word >> 4], word,
>> + value, size);
>> + switch (word >> 4) {
>> + case CH_SEMA:
>> + // mask the new semaphore value, as only the lowest 8 bits are RW
>> + s->channel[channel].r[CH_SEMA] =
>> + (oldvalue & ~0xff) |
>> + (s->channel[channel].r[CH_SEMA] & 0xff);
>
> You can do this with
> s->channel[channel].r[CH_SEMA] = deposit32(oldvalue, 0, 8,
> s->channel[channel].r[CH_SEMA]);
>
And more generally speaking, favor the extract32/deposit32 macros
rather than shift/mask.
>> + mxs_dma_ch_update(&s->channel[channel]);
>> + break;
>> + }
>> + } else {
>> + qemu_log_mask(LOG_GUEST_ERROR,
>> + "%s: bad offset 0x%x\n", __func__, (int) offset);
>> + }
>> + break;
>> + }
>> + switch (offset >> 4) {
>> + case DMA_CTRL0:
>> + if ((oldvalue ^ s->r[DMA_CTRL0]) == 0x80000000
>> + && !(oldvalue & 0x80000000)) {
>> + // printf("%s write reseting, anding clockgate\n", s->name);
>> + s->r[DMA_CTRL0] |= 0x40000000;
>> + }
>> + break;
>> + case DMA_CTRL1:
>> + for (i = 0; i < DMA_MAX_CHANNELS; i++)
>> + if (s->channel[i].r[CH_NEXTCMD] &&
>> + !(s->r[DMA_CTRL1] & (1 << i))) {
>> + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>> + /* add a bit of latency to the timer. Ideally would
>> + * do some calculation proportional to the transfer
>> + * size. TODO ?
>> + */
>> + timer_mod(s->channel[i].timer, now + 100000);
>> + }
>> + break;
>> + }
>> +}
>> +
>> +
>> +static const MemoryRegionOps mxs_dma_ops = {
>> + .read = mxs_dma_read,
>> + .write = mxs_dma_write,
>> + .endianness = DEVICE_NATIVE_ENDIAN,
>> +};
>> +
>> +static void mxs_dma_common_init(mxs_dma_state *s)
>> +{
>> + int i;
blank line here.
>> + memory_region_init_io(&s->iomem, OBJECT(s), &mxs_dma_ops, s, "mxs_dma", 0x2000);
>> + sysbus_init_mmio(&s->busdev, &s->iomem);
>> + for (i = 0; i < DMA_MAX_CHANNELS; i++) {
>> + s->channel[i].dma = s;
>> + s->channel[i].channel = i;
>> + s->channel[i].timer =
>> + timer_new_ns(QEMU_CLOCK_VIRTUAL, mxs_dma_ch_run, &s->channel[i]);
>> + }
>> +}
>> +
>> +static int mxs_apbh_dma_init(SysBusDevice *dev)
>> +{
>> + mxs_dma_state *s = OBJECT_CHECK(mxs_dma_state, dev, "mxs_apbh_dma");
>> +
>> + mxs_dma_common_init(s);
>> + s->name = "dma_apbh";
>> + s->base = 0x40;
>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SSP1].irq);
>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SSP2].irq);
>> + s->channel[MX23_DMA_SSP1].base = MX23_SSP1_BASE_ADDR;
>> + s->channel[MX23_DMA_SSP1].dataoffset = 0x70;
>> + s->channel[MX23_DMA_SSP2].base = MX23_SSP2_BASE_ADDR;
>> + s->channel[MX23_DMA_SSP2].dataoffset = 0x70;
>> +
>> + return 0;
>> +}
>> +
>> +static int mxs_apbx_dma_init(SysBusDevice *dev)
>> +{
>> +// mxs_dma_state *s = FROM_SYSBUS(mxs_dma_state, dev);
>> + mxs_dma_state *s = OBJECT_CHECK(mxs_dma_state, dev, "mxs_apbx_dma");
>> +
Define your own proper QOM cast macro rather than an inplace OBJECT_CHECK.
>> + mxs_dma_common_init(s);
>> + s->name = "dma_apbx";
>> + s->base = 0x100;
>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_ADC].irq);
>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_DAC].irq);
>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SPDIF].irq);
>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_I2C].irq);
>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SAIF0].irq);
>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_UART0_RX].irq);
>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_UART0_TX].irq);
>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_UART1_RX].irq);
>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_UART1_TX].irq);
So this is a little suspicious. It looks to me like your device is
system aware. A self contained DMA controller should not really know
what its DMAing for. Are the connections to I2C, UART and friends
actually specified in the DMA core documentation or is this SoC/Board
level connectivity?
>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SAIF1].irq);
>> +
>> + return 0;
>> +}
>> +
>> +static void mxs_apbh_dma_class_init(ObjectClass *klass, void *data)
>> +{
>> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
>> +
>> + sdc->init = mxs_apbh_dma_init;
use of SysBusDevice::init is depracted. Please use object::init or
Device::realize instead. Check the more recently committed device
models (Allwinner and Digic are two series that went in recently) for
examples of init styling.
>> +}
>> +
>> +static void mxs_apbx_dma_class_init(ObjectClass *klass, void *data)
>> +{
>> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
>> +
>> + sdc->init = mxs_apbx_dma_init;
>> +}
>
> Needs reset and vmstate.
>
>> +
>> +static TypeInfo apbh_dma_info = {
>> + .name = "mxs_apbh_dma",
>> + .parent = TYPE_SYS_BUS_DEVICE,
>> + .instance_size = sizeof(mxs_dma_state),
>> + .class_init = mxs_apbh_dma_class_init,
>> +};
Blank line.
>> +static TypeInfo apbx_dma_info = {
>> + .name = "mxs_apbx_dma",
>> + .parent = TYPE_SYS_BUS_DEVICE,
>> + .instance_size = sizeof(mxs_dma_state),
>> + .class_init = mxs_apbx_dma_class_init,
>> +};
>> +
As a general rule, you should have a QOM class for the common base
device. You then have two derived classes that add their little bit.
One functional reason for this, is without a common base it's
impossible to create a QOM cast macro for the shared functionality
(without an actual class to tie it to).
Regards,
Peter
>> +static void mxs_dma_register(void)
>> +{
>> + type_register_static(&apbh_dma_info);
>> + type_register_static(&apbx_dma_info);
>> +}
>> +
>> +type_init(mxs_dma_register)
>> --
>> 1.8.5.1
>
> thanks
> -- PMM
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 04/13] mxs/imx23: Add DMA driver
2014-01-10 0:52 ` Peter Crosthwaite
@ 2014-01-10 0:54 ` Peter Crosthwaite
2014-01-10 10:55 ` Peter Maydell
1 sibling, 0 replies; 36+ messages in thread
From: Peter Crosthwaite @ 2014-01-10 0:54 UTC (permalink / raw)
To: Peter Maydell; +Cc: Michel Pollet, QEMU Developers
On Fri, Jan 10, 2014 at 10:52 AM, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> On Tue, Jan 7, 2014 at 1:35 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> On 11 December 2013 13:56, Michel Pollet <buserror@gmail.com> wrote:
>>> This driver works sufficiently well that linux can use it to access
>>> the SD card using the SD->DMA->SSI->SD. It hasn't been tested for
>>> much else.
>>>
>>> Signed-off-by: Michel Pollet <buserror@gmail.com>
>>> ---
>>> hw/dma/Makefile.objs | 1 +
>>> hw/dma/mxs_dma.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>> 2 files changed, 348 insertions(+)
>>> create mode 100644 hw/dma/mxs_dma.c
>>>
>>> diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
>>> index 0e65ed0..3373aa1 100644
>>> --- a/hw/dma/Makefile.objs
>>> +++ b/hw/dma/Makefile.objs
>>> @@ -8,6 +8,7 @@ common-obj-$(CONFIG_XILINX_AXI) += xilinx_axidma.o
>>> common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o
>>> common-obj-$(CONFIG_STP2000) += sparc32_dma.o
>>> common-obj-$(CONFIG_SUN4M) += sun4m_iommu.o
>>> +common-obj-$(CONFIG_MXS) += mxs_dma.o
>>>
>>> obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
>>> obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
>>> diff --git a/hw/dma/mxs_dma.c b/hw/dma/mxs_dma.c
>>> new file mode 100644
>>> index 0000000..9ac787b
>>> --- /dev/null
>>> +++ b/hw/dma/mxs_dma.c
>>> @@ -0,0 +1,347 @@
>>> +/*
>>> + * mxs_dma.c
>>> + *
>>> + * Copyright: Michel Pollet <buserror@gmail.com>
>>> + *
>>> + * QEMU Licence
>>> + */
>>> +
>>> +/*
>>> + * Implements the DMA block of the mxs.
>>> + * The current implementation can run chains of commands etc, however it's only
>>> + * been tested with SSP for SD/MMC card access. It ought to work with normal SPI
>>> + * too, and possibly other peripherals, however it's entirely untested
>>> + */
>>> +#include "hw/sysbus.h"
>>> +#include "hw/arm/mxs.h"
>>> +
>>> +/*
>>> + * DMA IO block register numbers
>>> + */
>>> +enum {
>>> + DMA_CTRL0 = 0x0,
>>> + DMA_CTRL1 = 0x1,
>>> + DMA_CTRL2 = 0x2,
>>> + DMA_DEVSEL1 = 0x3,
>>> + DMA_DEVSEL2 = 0x4,
>>> + DMA_MAX,
>>> +
>>> + /*
>>> + * The DMA block for APBH and APBX have a different base address,
>>> + * but they share a 7 words stride between channels.
>>> + */
>>> + DMA_STRIDE = 0x70,
>>> + /*
>>> + * Neither blocks uses that many, but there is space for them...
>>> + */
>>> + DMA_MAX_CHANNELS = 16,
>>> +};
>>> +
>>> +/*
>>> + * DMA channel register numbers
>>> + */
>>> +enum {
>>> + CH_CURCMD = 0,
>>> + CH_NEXTCMD = 1,
>>> + CH_CMD = 2,
>>> + CH_BUFFER_ADDR = 3,
>>> + CH_SEMA = 4,
>>> + CH_DEBUG1 = 5,
>>> + CH_DEBUG2 = 6,
>>> +};
>>> +
>>> +/*
>>> + * Channel command bit numbers
>>> + */
>>> +enum {
>>> + CH_CMD_IRQ_COMPLETE = 3,
>>> + CH_CMD_SEMAPHORE = 6,
>>> +};
>>> +
>>> +/*
>>> + * nicked from linux
>>
>> You don't need to say that, it doesn't really add any
>> information to the reader.
>>
>>> + * this is the memory representation of a DMA request
>>> + */
>>> +struct mxs_dma_ccw {
>>> + uint32_t next;
>>> + uint16_t bits;
>>> + uint16_t xfer_bytes;
>>> +#define MAX_XFER_BYTES 0xff00
>>
>> I'd rather have the #defines before the struct than
>> interleaved, personally.
>>
>
> TBH, this is the same as my own preferred personal coding style (and I
> refactor on upstreaming due to it's contention). I'd like to push for
> it's general acceptance. #defining at the point of relevance is a well
> adopted concept and makes code much more readable.
>
>>> + uint32_t bufaddr;
>>> +#define MXS_PIO_WORDS 16
>>> + uint32_t pio_words[MXS_PIO_WORDS];
>>> +}__attribute__((packed));
>>
>> If you need to use packed then always use the QEMU_PACKED
>> macro; this ensures the same layout on all host platforms
>> (Windows behaviour of plain attribute packed is different).
>>
>>
>>> +
>>> +/*
>>> + * Per channel DMA description
>>> + */
>>> +typedef struct mxs_dma_channel {
>>> + QEMUTimer *timer;
>>> + struct mxs_dma_state *dma;
>>> + int channel; // channel index
>>> + hwaddr base; // base of peripheral
>>> + hwaddr dataoffset; // offset of the true in/out data latch register
>>
>> No C++-style comments, please.
>>
>>> + uint32_t r[10];
>>> + qemu_irq irq;
>>> +} mxs_dma_channel;
>>> +
>>> +
>>> +typedef struct mxs_dma_state {
>>> + SysBusDevice busdev;
>
> parent_obj. Check your QOM style.
>
> http://lists.gnu.org/archive/html/qemu-devel/2013-02/msg05045.html
>
Sry bad link, try this:
http://wiki.qemu.org/QOMConventions
Regards,
Peter
>>> + MemoryRegion iomem;
>>> + const char * name;
>>> +
>>> + struct soc_dma_s * dma;
>>> + uint32_t r[DMA_MAX];
>>> +
>>> + hwaddr base; // base of peripheral
>>> + mxs_dma_channel channel[DMA_MAX_CHANNELS];
>>> +} mxs_dma_state;
>>> +
>>> +static void mxs_dma_ch_update(mxs_dma_channel *s)
>>> +{
>>> + struct mxs_dma_ccw req;
>>> + int i;
>>> +
>>> + /* increment the semaphore, if needed */
>>> + s->r[CH_SEMA] = (((s->r[CH_SEMA] >> 16) & 0xff) +
>>> + (s->r[CH_SEMA] & 0xff)) << 16;
>>
>> This is confusing -- what's it actually doing?
>>
>>> + if (!((s->r[CH_SEMA] >> 16) & 0xff)) {
>>> + return;
>>> + }
>>> + /* read the request from memory */
>>> + cpu_physical_memory_read(s->r[CH_NEXTCMD], &req, sizeof(req));
>>
>> This will not work if your host and target have opposite
>> endianness. If you're going to read the whole struct in
>> at once like this you need to use the cpu_to_le* functions
>> to swap all its members to the host endianness.
>> Similarly anywhere you write a block of memory back.
>> Alternatively you can use the ld*_le_phys and st*_le_phys
>> to read and write specifically sized bytes/shorts/words
>> to little-endian guest order.
>>
>>> + /* update the latch registers accordingly */
>>> + s->r[CH_CURCMD] = s->r[CH_NEXTCMD];
>>> + s->r[CH_NEXTCMD] = req.next;
>>> + s->r[CH_CMD] = (req.xfer_bytes << 16) | req.bits;
>>> + s->r[CH_BUFFER_ADDR] = req.bufaddr;
>>> +
>>> + /* write PIO registers first, if any */
>>> + for (i = 0; i < (req.bits >> 12); i++) {
>>> + cpu_physical_memory_rw(s->base + (i << 4),
>>> + (uint8_t*) &req.pio_words[i], 4, 1);
>>
>> ...for instance this is probably best done via
>> stl_le_phys() to write each word to the guest memory.
>>
>
> Last I knew, dma_memory_read|write is the correct way to do DMA from
> device land. cpu_physical_memory and stl_ are more CPU concepts.
> dma_memory_read|write has the added advantage of accepting and
> address_space which allows for DMA in machines with non-flat
> bus/memory layouts.
>
>>> + }
>>> + /* next handle any "data" requests */
>>> + switch (req.bits & 0x3) {
>>> + case 0:
>>> + break; // PIO only
>>> + case 0x1: { // WRITE (from periph to memory)
>>> + uint32_t buf = req.bufaddr;
>>> + uint8_t b = 0;
>>> + while (req.xfer_bytes--) {
>>> + cpu_physical_memory_rw(s->base + s->dataoffset, &b, 1, 0);
>>> + cpu_physical_memory_rw(buf, &b, 1, 1);
>>> + buf++;
>>> + }
>>> + } break;
>>> + case 0x2: { // READ (from memory to periph)
>>> + uint32_t buf = req.bufaddr;
>>> + uint8_t b = 0;
>>> + while (req.xfer_bytes--) {
>>> + cpu_physical_memory_rw(buf, &b, 1, 0);
>>> + cpu_physical_memory_rw(s->base + s->dataoffset, &b, 1, 1);
>>> + buf++;
>>> + }
>>> + } break;
>>> + }
>>> +
>>> + s->dma->r[DMA_CTRL1] |= 1 << s->channel;
>>> + /* trigger IRQ if requested */
>>> + if ((s->dma->r[DMA_CTRL1] >> 16) & (1 << s->channel)) {
>>> + if (req.bits & (1 << CH_CMD_IRQ_COMPLETE)) {
>>> + qemu_set_irq(s->irq, 1);
>>> + }
>>> + }
>>> +
>>> + /* decrement semaphore if requested */
>>> + if (s->r[CH_CMD] & (1 << CH_CMD_SEMAPHORE)) {
>>> + s->r[CH_SEMA] = (((s->r[CH_SEMA] >> 16) & 0xff) - 1) << 16;
>>> + }
>>> + /* If the semaphore is still on, try to trigger a chained request */
>>> + if ((s->r[CH_SEMA] >> 16) & 0xff) {
>>> + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>> + timer_mod(s->timer, now + 10);
>>
>> This stuff involving the timer looks a bit fishy, but I'm
>> not really up on current best practice with DMA, timers, etc.
>> Peter Crosthwaite may have a more informed opinion.
>>
>
> I'll take a step back and do a full series review. As of this mail all
> i've done is a quick style scan, so ill go more in-depth on either
> this V2 or this if Michel respins. Give me a few days. Don't wait for
> me, I'd prefer to look at v2 as PMM has already asked a good number of
> changes. cc me for a speedy review.
>
>>> + }
>>> +}
>>> +
>>> +/* called on one shot timer activation */
>>> +static void mxs_dma_ch_run(void *opaque)
>>> +{
>>> + mxs_dma_channel *s = opaque;
>>> + mxs_dma_ch_update(s);
>>> +}
>>> +
>>> +static uint64_t mxs_dma_read(void *opaque, hwaddr offset, unsigned size)
>>> +{
>>> + mxs_dma_state *s = (mxs_dma_state *) opaque;
>>> + uint32_t res = 0;
>>> +
>>> + switch (offset >> 4) {
>>> + case 0 ... DMA_MAX - 1:
>>> + res = s->r[offset >> 4];
>>> + break;
>>> + default:
>>> + if (offset >= s->base) {
>>> + offset -= s->base;
>>> + int channel = offset / DMA_STRIDE;
>>> + int word = (offset % DMA_STRIDE) >> 4;
>>> + res = s->channel[channel].r[word];
>>> + } else
>>> + qemu_log_mask(LOG_GUEST_ERROR,
>>> + "%s: bad offset 0x%x\n", __func__, (int) offset);
>>> + break;
>>> + }
>>> +
>>> + return res;
>>> +}
>>> +
>>> +static void mxs_dma_write(void *opaque, hwaddr offset, uint64_t value,
>>> + unsigned size)
>>> +{
>>> + mxs_dma_state *s = (mxs_dma_state *) opaque;
>>> + uint32_t oldvalue = 0;
>>> + int channel, word, i;
>>> +
>>> + switch (offset >> 4) {
>>> + case 0 ... DMA_MAX - 1:
>>> + oldvalue = mxs_write(&s->r[offset >> 4], offset, value, size);
>>> + break;
>>> + default:
>>> + if (offset >= s->base) {
>>> + channel = (offset - s->base) / DMA_STRIDE;
>>> + word = (offset - s->base) % DMA_STRIDE;
>>> + oldvalue = mxs_write(
>>> + &s->channel[channel].r[word >> 4], word,
>>> + value, size);
>>> + switch (word >> 4) {
>>> + case CH_SEMA:
>>> + // mask the new semaphore value, as only the lowest 8 bits are RW
>>> + s->channel[channel].r[CH_SEMA] =
>>> + (oldvalue & ~0xff) |
>>> + (s->channel[channel].r[CH_SEMA] & 0xff);
>>
>> You can do this with
>> s->channel[channel].r[CH_SEMA] = deposit32(oldvalue, 0, 8,
>> s->channel[channel].r[CH_SEMA]);
>>
>
> And more generally speaking, favor the extract32/deposit32 macros
> rather than shift/mask.
>
>>> + mxs_dma_ch_update(&s->channel[channel]);
>>> + break;
>>> + }
>>> + } else {
>>> + qemu_log_mask(LOG_GUEST_ERROR,
>>> + "%s: bad offset 0x%x\n", __func__, (int) offset);
>>> + }
>>> + break;
>>> + }
>>> + switch (offset >> 4) {
>>> + case DMA_CTRL0:
>>> + if ((oldvalue ^ s->r[DMA_CTRL0]) == 0x80000000
>>> + && !(oldvalue & 0x80000000)) {
>>> + // printf("%s write reseting, anding clockgate\n", s->name);
>>> + s->r[DMA_CTRL0] |= 0x40000000;
>>> + }
>>> + break;
>>> + case DMA_CTRL1:
>>> + for (i = 0; i < DMA_MAX_CHANNELS; i++)
>>> + if (s->channel[i].r[CH_NEXTCMD] &&
>>> + !(s->r[DMA_CTRL1] & (1 << i))) {
>>> + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>> + /* add a bit of latency to the timer. Ideally would
>>> + * do some calculation proportional to the transfer
>>> + * size. TODO ?
>>> + */
>>> + timer_mod(s->channel[i].timer, now + 100000);
>>> + }
>>> + break;
>>> + }
>>> +}
>>> +
>>> +
>>> +static const MemoryRegionOps mxs_dma_ops = {
>>> + .read = mxs_dma_read,
>>> + .write = mxs_dma_write,
>>> + .endianness = DEVICE_NATIVE_ENDIAN,
>>> +};
>>> +
>>> +static void mxs_dma_common_init(mxs_dma_state *s)
>>> +{
>>> + int i;
>
> blank line here.
>
>>> + memory_region_init_io(&s->iomem, OBJECT(s), &mxs_dma_ops, s, "mxs_dma", 0x2000);
>>> + sysbus_init_mmio(&s->busdev, &s->iomem);
>>> + for (i = 0; i < DMA_MAX_CHANNELS; i++) {
>>> + s->channel[i].dma = s;
>>> + s->channel[i].channel = i;
>>> + s->channel[i].timer =
>>> + timer_new_ns(QEMU_CLOCK_VIRTUAL, mxs_dma_ch_run, &s->channel[i]);
>>> + }
>>> +}
>>> +
>>> +static int mxs_apbh_dma_init(SysBusDevice *dev)
>>> +{
>>> + mxs_dma_state *s = OBJECT_CHECK(mxs_dma_state, dev, "mxs_apbh_dma");
>>> +
>>> + mxs_dma_common_init(s);
>>> + s->name = "dma_apbh";
>>> + s->base = 0x40;
>>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SSP1].irq);
>>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SSP2].irq);
>>> + s->channel[MX23_DMA_SSP1].base = MX23_SSP1_BASE_ADDR;
>>> + s->channel[MX23_DMA_SSP1].dataoffset = 0x70;
>>> + s->channel[MX23_DMA_SSP2].base = MX23_SSP2_BASE_ADDR;
>>> + s->channel[MX23_DMA_SSP2].dataoffset = 0x70;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int mxs_apbx_dma_init(SysBusDevice *dev)
>>> +{
>>> +// mxs_dma_state *s = FROM_SYSBUS(mxs_dma_state, dev);
>>> + mxs_dma_state *s = OBJECT_CHECK(mxs_dma_state, dev, "mxs_apbx_dma");
>>> +
>
> Define your own proper QOM cast macro rather than an inplace OBJECT_CHECK.
>
>>> + mxs_dma_common_init(s);
>>> + s->name = "dma_apbx";
>>> + s->base = 0x100;
>>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_ADC].irq);
>>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_DAC].irq);
>>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SPDIF].irq);
>>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_I2C].irq);
>>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SAIF0].irq);
>>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_UART0_RX].irq);
>>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_UART0_TX].irq);
>>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_UART1_RX].irq);
>>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_UART1_TX].irq);
>
> So this is a little suspicious. It looks to me like your device is
> system aware. A self contained DMA controller should not really know
> what its DMAing for. Are the connections to I2C, UART and friends
> actually specified in the DMA core documentation or is this SoC/Board
> level connectivity?
>
>>> + sysbus_init_irq(dev, &s->channel[MX23_DMA_SAIF1].irq);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void mxs_apbh_dma_class_init(ObjectClass *klass, void *data)
>>> +{
>>> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
>>> +
>>> + sdc->init = mxs_apbh_dma_init;
>
> use of SysBusDevice::init is depracted. Please use object::init or
> Device::realize instead. Check the more recently committed device
> models (Allwinner and Digic are two series that went in recently) for
> examples of init styling.
>
>>> +}
>>> +
>>> +static void mxs_apbx_dma_class_init(ObjectClass *klass, void *data)
>>> +{
>>> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
>>> +
>>> + sdc->init = mxs_apbx_dma_init;
>>> +}
>>
>> Needs reset and vmstate.
>>
>>> +
>>> +static TypeInfo apbh_dma_info = {
>>> + .name = "mxs_apbh_dma",
>>> + .parent = TYPE_SYS_BUS_DEVICE,
>>> + .instance_size = sizeof(mxs_dma_state),
>>> + .class_init = mxs_apbh_dma_class_init,
>>> +};
>
> Blank line.
>
>>> +static TypeInfo apbx_dma_info = {
>>> + .name = "mxs_apbx_dma",
>>> + .parent = TYPE_SYS_BUS_DEVICE,
>>> + .instance_size = sizeof(mxs_dma_state),
>>> + .class_init = mxs_apbx_dma_class_init,
>>> +};
>>> +
>
> As a general rule, you should have a QOM class for the common base
> device. You then have two derived classes that add their little bit.
> One functional reason for this, is without a common base it's
> impossible to create a QOM cast macro for the shared functionality
> (without an actual class to tie it to).
>
> Regards,
> Peter
>
>>> +static void mxs_dma_register(void)
>>> +{
>>> + type_register_static(&apbh_dma_info);
>>> + type_register_static(&apbx_dma_info);
>>> +}
>>> +
>>> +type_init(mxs_dma_register)
>>> --
>>> 1.8.5.1
>>
>> thanks
>> -- PMM
>>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 04/13] mxs/imx23: Add DMA driver
2014-01-10 0:52 ` Peter Crosthwaite
2014-01-10 0:54 ` Peter Crosthwaite
@ 2014-01-10 10:55 ` Peter Maydell
1 sibling, 0 replies; 36+ messages in thread
From: Peter Maydell @ 2014-01-10 10:55 UTC (permalink / raw)
To: Peter Crosthwaite; +Cc: Michel Pollet, QEMU Developers
On 10 January 2014 00:52, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> On Tue, Jan 7, 2014 at 1:35 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> I'd rather have the #defines before the struct than
>> interleaved, personally.
>>
>
> TBH, this is the same as my own preferred personal coding style (and I
> refactor on upstreaming due to it's contention). I'd like to push for
> it's general acceptance. #defining at the point of relevance is a well
> adopted concept and makes code much more readable.
I find it kind of weird because it is making use of
the fact that the preprocessor really is a separate
textual step, when it doesn't actually need to. If
we were using enum or 'const int' here you wouldn't
be able to interleave like this and I don't think
you'd be complaining that the language wasn't flexible
enough.
That said, this is really a very minor point as far as
I'm concerned so if people think it's reasonable to do
I'm happy to let it pass.
>> ...for instance this is probably best done via
>> stl_le_phys() to write each word to the guest memory.
>>
>
> Last I knew, dma_memory_read|write is the correct way to do DMA from
> device land. cpu_physical_memory and stl_ are more CPU concepts.
> dma_memory_read|write has the added advantage of accepting and
> address_space which allows for DMA in machines with non-flat
> bus/memory layouts.
You're probably right, yes. I said I wasn't particularly
up on our current practices for DMA :-)
thanks
-- PMM
^ permalink raw reply [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 05/13] mxs/imx23: Add the interrupt collector
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
` (3 preceding siblings ...)
2013-12-11 13:56 ` [Qemu-devel] [PATCH 04/13] mxs/imx23: Add DMA driver Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2014-01-06 15:41 ` Peter Maydell
2014-01-11 8:29 ` Peter Crosthwaite
2013-12-11 13:56 ` [Qemu-devel] [PATCH 06/13] mxs/imx23: Add digctl driver Michel Pollet
` (8 subsequent siblings)
13 siblings, 2 replies; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
Implements the interrupt collector IO block
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/intc/Makefile.objs | 1 +
hw/intc/mxs_icoll.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 201 insertions(+)
create mode 100644 hw/intc/mxs_icoll.c
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 47ac442..e934b3c 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -24,3 +24,4 @@ obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
obj-$(CONFIG_SH4) += sh_intc.o
obj-$(CONFIG_XICS) += xics.o
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
+obj-$(CONFIG_MXS) += mxs_icoll.o
diff --git a/hw/intc/mxs_icoll.c b/hw/intc/mxs_icoll.c
new file mode 100644
index 0000000..a1fd7d9
--- /dev/null
+++ b/hw/intc/mxs_icoll.c
@@ -0,0 +1,200 @@
+/*
+ * mxs_icoll.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ */
+
+/*
+ * This block implements the interrupt collector of the mxs
+ * Currently no priority is handled, as linux doesn't use them anyway
+ */
+
+#include "hw/sysbus.h"
+#include "hw/arm/mxs.h"
+
+enum {
+ ICOLL_VECTOR = 0,
+ ICOLL_LEVELACK = 1,
+ ICOLL_CTRL = 2,
+ // 3, reserved?
+ ICOLL_VBASE = 4,
+ ICOLL_STAT = 7,
+
+ ICOLL_REG_MAX,
+
+ ICOLL_RAW0 = 0xa,
+ ICOLL_RAW1,
+ ICOLL_RAW2,
+ ICOLL_RAW3,
+
+ ICOLL_INT0 = 0x12,
+ ICOLL_INT127 = 0x91,
+};
+
+typedef struct mxs_icoll_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t reg[ICOLL_REG_MAX];
+
+ uint32_t raised[4];
+ uint32_t fiq[4];
+ uint32_t irq[4];
+
+ uint8_t r[128];
+
+ qemu_irq parent_irq;
+ qemu_irq parent_fiq;
+} mxs_icoll_state;
+
+static void mxs_icoll_update(mxs_icoll_state *s)
+{
+ int fiq = 0, irq = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ int id = ffs(s->raised[i]);
+ int vector = (i * 32) + id - 1;
+ if (s->raised[i] & s->fiq[i]) {
+ fiq++;
+ s->reg[ICOLL_STAT] = vector;
+ break;
+ }
+ if (s->raised[i] & s->irq[i]) {
+ irq++;
+ s->reg[ICOLL_STAT] = vector;
+ break;
+ }
+ }
+ qemu_set_irq(s->parent_irq, irq != 0);
+ qemu_set_irq(s->parent_fiq, fiq != 0);
+}
+
+static void mxs_icoll_set_irq(void *opaque, int irq, int level)
+{
+ mxs_icoll_state *s = (mxs_icoll_state *) opaque;
+ if (level)
+ s->raised[(irq / 32)] |= 1 << (irq % 32);
+ else
+ s->raised[(irq / 32)] &= ~(1 << (irq % 32));
+ mxs_icoll_update(s);
+}
+
+static uint64_t mxs_icoll_read(void *opaque, hwaddr offset, unsigned size)
+{
+ mxs_icoll_state *s = (mxs_icoll_state *) opaque;
+
+ switch (offset >> 4) {
+ case 0 ... ICOLL_REG_MAX:
+ return s->reg[offset >> 4];
+ case ICOLL_RAW0 ... ICOLL_RAW3:
+ return s->raised[(offset >> 4) - ICOLL_RAW0];
+ case ICOLL_INT0 ... ICOLL_INT127:
+ return s->r[(offset >> 4) - ICOLL_INT0];
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ return 0;
+}
+
+static void mxs_icoll_write(
+ void *opaque, hwaddr offset, uint64_t value, unsigned size)
+{
+ mxs_icoll_state *s = (mxs_icoll_state *) opaque;
+ uint32_t irqval, irqi = 0;
+ uint32_t * dst = NULL;
+ uint32_t oldvalue = 0;
+
+ switch (offset >> 4) {
+ case 0 ... ICOLL_REG_MAX:
+ dst = s->reg + (offset >> 4);
+ break;
+ case ICOLL_INT0 ... ICOLL_INT127:
+ irqi = (offset >> 4) - ICOLL_INT0;
+ irqval = s->r[irqi];
+ dst = &irqval;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ if (!dst) {
+ return;
+ }
+ oldvalue = mxs_write(dst, offset, value, size);
+
+ switch (offset >> 4) {
+ case ICOLL_CTRL:
+ if ((oldvalue ^ s->r[ICOLL_CTRL]) == 0x80000000
+ && !(oldvalue & 0x80000000)) {
+ // printf("%s reseting, anding clockgate\n", __func__);
+ s->r[ICOLL_CTRL] |= 0x40000000;
+ }
+ break;
+ case ICOLL_LEVELACK:
+ irqi = s->reg[ICOLL_STAT] & 0x7f;
+ s->raised[(irqi / 32)] &= ~(1 << (irqi % 32));
+ s->reg[ICOLL_STAT] = 0x7f;
+ break;
+ case ICOLL_INT0 ... ICOLL_INT127:
+ s->r[irqi] = irqval & ~(0x40); // dont' set softirq bit
+ if (irqval & 0x4) // ENABLE
+ s->irq[irqi / 32] |= (1 << (irqi % 32));
+ else
+ s->irq[irqi / 32] &= ~(1 << (irqi % 32));
+ if (irqval & 0x10) // ENFIQ
+ s->fiq[irqi / 32] |= (1 << (irqi % 32));
+ else
+ s->fiq[irqi / 32] &= ~(1 << (irqi % 32));
+ if (irqval & 0x8) // SOFTIRQ
+ mxs_icoll_set_irq(s, irqi, 1);
+ break;
+ }
+
+ mxs_icoll_update(s);
+}
+
+static const MemoryRegionOps mxs_icoll_ops = {
+ .read = mxs_icoll_read,
+ .write = mxs_icoll_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int mxs_icoll_init(SysBusDevice *dev)
+{
+ mxs_icoll_state *s = OBJECT_CHECK(mxs_icoll_state, dev, "mxs_icoll");
+ DeviceState *qdev = DEVICE(dev);
+
+ qdev_init_gpio_in(qdev, mxs_icoll_set_irq, 128);
+ sysbus_init_irq(dev, &s->parent_irq);
+ sysbus_init_irq(dev, &s->parent_fiq);
+ memory_region_init_io(&s->iomem, OBJECT(s), &mxs_icoll_ops, s,
+ "mxs_icoll", 0x2000);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+static void mxs_icoll_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = mxs_icoll_init;
+}
+
+static TypeInfo icoll_info = {
+ .name = "mxs_icoll",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mxs_icoll_state),
+ .class_init = mxs_icoll_class_init,
+};
+
+static void mxs_icoll_register(void)
+{
+ type_register_static(&icoll_info);
+}
+
+type_init(mxs_icoll_register)
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 05/13] mxs/imx23: Add the interrupt collector
2013-12-11 13:56 ` [Qemu-devel] [PATCH 05/13] mxs/imx23: Add the interrupt collector Michel Pollet
@ 2014-01-06 15:41 ` Peter Maydell
2014-01-11 8:29 ` Peter Crosthwaite
1 sibling, 0 replies; 36+ messages in thread
From: Peter Maydell @ 2014-01-06 15:41 UTC (permalink / raw)
To: Michel Pollet; +Cc: QEMU Developers
On 11 December 2013 13:56, Michel Pollet <buserror@gmail.com> wrote:
> Implements the interrupt collector IO block
>
> Signed-off-by: Michel Pollet <buserror@gmail.com>
> ---
> hw/intc/Makefile.objs | 1 +
> hw/intc/mxs_icoll.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 201 insertions(+)
> create mode 100644 hw/intc/mxs_icoll.c
>
> diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
> index 47ac442..e934b3c 100644
> --- a/hw/intc/Makefile.objs
> +++ b/hw/intc/Makefile.objs
> @@ -24,3 +24,4 @@ obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
> obj-$(CONFIG_SH4) += sh_intc.o
> obj-$(CONFIG_XICS) += xics.o
> obj-$(CONFIG_XICS_KVM) += xics_kvm.o
> +obj-$(CONFIG_MXS) += mxs_icoll.o
> diff --git a/hw/intc/mxs_icoll.c b/hw/intc/mxs_icoll.c
> new file mode 100644
> index 0000000..a1fd7d9
> --- /dev/null
> +++ b/hw/intc/mxs_icoll.c
> @@ -0,0 +1,200 @@
> +/*
> + * mxs_icoll.c
> + *
> + * Copyright: Michel Pollet <buserror@gmail.com>
> + *
> + * QEMU Licence
> + */
I'm not going to keep making the same remarks about reset,
licence header, coding style, vmstate, but you can assume
they apply to all these patches.
> +
> +/*
> + * This block implements the interrupt collector of the mxs
> + * Currently no priority is handled, as linux doesn't use them anyway
> + */
> +
> +#include "hw/sysbus.h"
> +#include "hw/arm/mxs.h"
> +
> +enum {
> + ICOLL_VECTOR = 0,
> + ICOLL_LEVELACK = 1,
> + ICOLL_CTRL = 2,
> + // 3, reserved?
> + ICOLL_VBASE = 4,
> + ICOLL_STAT = 7,
> +
> + ICOLL_REG_MAX,
> +
> + ICOLL_RAW0 = 0xa,
> + ICOLL_RAW1,
> + ICOLL_RAW2,
> + ICOLL_RAW3,
> +
> + ICOLL_INT0 = 0x12,
> + ICOLL_INT127 = 0x91,
> +};
> +
> +typedef struct mxs_icoll_state {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> + uint32_t reg[ICOLL_REG_MAX];
> +
> + uint32_t raised[4];
> + uint32_t fiq[4];
> + uint32_t irq[4];
> +
> + uint8_t r[128];
> +
> + qemu_irq parent_irq;
> + qemu_irq parent_fiq;
> +} mxs_icoll_state;
> +
> +static void mxs_icoll_update(mxs_icoll_state *s)
> +{
> + int fiq = 0, irq = 0;
> + int i;
> +
> + for (i = 0; i < 4; i++) {
> + int id = ffs(s->raised[i]);
> + int vector = (i * 32) + id - 1;
> + if (s->raised[i] & s->fiq[i]) {
> + fiq++;
> + s->reg[ICOLL_STAT] = vector;
> + break;
> + }
> + if (s->raised[i] & s->irq[i]) {
> + irq++;
> + s->reg[ICOLL_STAT] = vector;
> + break;
> + }
> + }
> + qemu_set_irq(s->parent_irq, irq != 0);
> + qemu_set_irq(s->parent_fiq, fiq != 0);
> +}
> +
> +static void mxs_icoll_set_irq(void *opaque, int irq, int level)
> +{
> + mxs_icoll_state *s = (mxs_icoll_state *) opaque;
> + if (level)
> + s->raised[(irq / 32)] |= 1 << (irq % 32);
> + else
> + s->raised[(irq / 32)] &= ~(1 << (irq % 32));
This if needs braces around both arms.
> + mxs_icoll_update(s);
> +}
> +
> +static uint64_t mxs_icoll_read(void *opaque, hwaddr offset, unsigned size)
> +{
> + mxs_icoll_state *s = (mxs_icoll_state *) opaque;
> +
> + switch (offset >> 4) {
> + case 0 ... ICOLL_REG_MAX:
> + return s->reg[offset >> 4];
> + case ICOLL_RAW0 ... ICOLL_RAW3:
> + return s->raised[(offset >> 4) - ICOLL_RAW0];
> + case ICOLL_INT0 ... ICOLL_INT127:
> + return s->r[(offset >> 4) - ICOLL_INT0];
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> + return 0;
> +}
> +
> +static void mxs_icoll_write(
> + void *opaque, hwaddr offset, uint64_t value, unsigned size)
> +{
> + mxs_icoll_state *s = (mxs_icoll_state *) opaque;
> + uint32_t irqval, irqi = 0;
> + uint32_t * dst = NULL;
> + uint32_t oldvalue = 0;
> +
> + switch (offset >> 4) {
> + case 0 ... ICOLL_REG_MAX:
> + dst = s->reg + (offset >> 4);
> + break;
> + case ICOLL_INT0 ... ICOLL_INT127:
> + irqi = (offset >> 4) - ICOLL_INT0;
> + irqval = s->r[irqi];
> + dst = &irqval;
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> + if (!dst) {
> + return;
> + }
> + oldvalue = mxs_write(dst, offset, value, size);
> +
> + switch (offset >> 4) {
> + case ICOLL_CTRL:
> + if ((oldvalue ^ s->r[ICOLL_CTRL]) == 0x80000000
> + && !(oldvalue & 0x80000000)) {
> + // printf("%s reseting, anding clockgate\n", __func__);
> + s->r[ICOLL_CTRL] |= 0x40000000;
> + }
I've seen this bit of magic in several patches now. It could
use explanation and maybe being factored out somehow?
> + break;
> + case ICOLL_LEVELACK:
> + irqi = s->reg[ICOLL_STAT] & 0x7f;
> + s->raised[(irqi / 32)] &= ~(1 << (irqi % 32));
> + s->reg[ICOLL_STAT] = 0x7f;
> + break;
> + case ICOLL_INT0 ... ICOLL_INT127:
> + s->r[irqi] = irqval & ~(0x40); // dont' set softirq bit
> + if (irqval & 0x4) // ENABLE
> + s->irq[irqi / 32] |= (1 << (irqi % 32));
> + else
> + s->irq[irqi / 32] &= ~(1 << (irqi % 32));
> + if (irqval & 0x10) // ENFIQ
> + s->fiq[irqi / 32] |= (1 << (irqi % 32));
> + else
> + s->fiq[irqi / 32] &= ~(1 << (irqi % 32));
> + if (irqval & 0x8) // SOFTIRQ
> + mxs_icoll_set_irq(s, irqi, 1);
> + break;
> + }
> +
> + mxs_icoll_update(s);
> +}
> +
> +static const MemoryRegionOps mxs_icoll_ops = {
> + .read = mxs_icoll_read,
> + .write = mxs_icoll_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static int mxs_icoll_init(SysBusDevice *dev)
> +{
> + mxs_icoll_state *s = OBJECT_CHECK(mxs_icoll_state, dev, "mxs_icoll");
> + DeviceState *qdev = DEVICE(dev);
> +
> + qdev_init_gpio_in(qdev, mxs_icoll_set_irq, 128);
> + sysbus_init_irq(dev, &s->parent_irq);
> + sysbus_init_irq(dev, &s->parent_fiq);
> + memory_region_init_io(&s->iomem, OBJECT(s), &mxs_icoll_ops, s,
> + "mxs_icoll", 0x2000);
> + sysbus_init_mmio(dev, &s->iomem);
> + return 0;
> +}
> +
> +static void mxs_icoll_class_init(ObjectClass *klass, void *data)
> +{
> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +
> + sdc->init = mxs_icoll_init;
> +}
> +
> +static TypeInfo icoll_info = {
> + .name = "mxs_icoll",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(mxs_icoll_state),
> + .class_init = mxs_icoll_class_init,
> +};
> +
> +static void mxs_icoll_register(void)
> +{
> + type_register_static(&icoll_info);
> +}
> +
> +type_init(mxs_icoll_register)
> --
> 1.8.5.1
>
>
-- PMM
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 05/13] mxs/imx23: Add the interrupt collector
2013-12-11 13:56 ` [Qemu-devel] [PATCH 05/13] mxs/imx23: Add the interrupt collector Michel Pollet
2014-01-06 15:41 ` Peter Maydell
@ 2014-01-11 8:29 ` Peter Crosthwaite
1 sibling, 0 replies; 36+ messages in thread
From: Peter Crosthwaite @ 2014-01-11 8:29 UTC (permalink / raw)
To: Michel Pollet; +Cc: qemu-devel@nongnu.org Developers
---------- Forwarded message ----------
From: Michel Pollet <buserror@gmail.com>
Date: Wed, Dec 11, 2013 at 11:56 PM
Subject: [Qemu-devel] [PATCH 05/13] mxs/imx23: Add the interrupt collector
To: qemu-devel@nongnu.org
Cc: Michel Pollet <buserror@gmail.com>
Implements the interrupt collector IO block
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/intc/Makefile.objs | 1 +
hw/intc/mxs_icoll.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 201 insertions(+)
create mode 100644 hw/intc/mxs_icoll.c
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index 47ac442..e934b3c 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -24,3 +24,4 @@ obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
obj-$(CONFIG_SH4) += sh_intc.o
obj-$(CONFIG_XICS) += xics.o
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
+obj-$(CONFIG_MXS) += mxs_icoll.o
diff --git a/hw/intc/mxs_icoll.c b/hw/intc/mxs_icoll.c
new file mode 100644
index 0000000..a1fd7d9
--- /dev/null
+++ b/hw/intc/mxs_icoll.c
@@ -0,0 +1,200 @@
+/*
+ * mxs_icoll.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ */
+
+/*
+ * This block implements the interrupt collector of the mxs
+ * Currently no priority is handled, as linux doesn't use them anyway
+ */
+
+#include "hw/sysbus.h"
+#include "hw/arm/mxs.h"
+
+enum {
+ ICOLL_VECTOR = 0,
+ ICOLL_LEVELACK = 1,
+ ICOLL_CTRL = 2,
+ // 3, reserved?
Any documentation with an answer? I'd just drop the comment TBH,
+ ICOLL_VBASE = 4,
+ ICOLL_STAT = 7,
+
+ ICOLL_REG_MAX,
+
+ ICOLL_RAW0 = 0xa,
+ ICOLL_RAW1,
+ ICOLL_RAW2,
+ ICOLL_RAW3,
+
+ ICOLL_INT0 = 0x12,
+ ICOLL_INT127 = 0x91,
Calculate ICOLL_INT127 arithmetically to be self documenting.
+};
+
+typedef struct mxs_icoll_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t reg[ICOLL_REG_MAX];
+
+ uint32_t raised[4];
+ uint32_t fiq[4];
+ uint32_t irq[4];
+
+ uint8_t r[128];
Magic numbers "128" and "4" could use macro definition.
+
+ qemu_irq parent_irq;
+ qemu_irq parent_fiq;
+} mxs_icoll_state;
+
+static void mxs_icoll_update(mxs_icoll_state *s)
+{
+ int fiq = 0, irq = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ int id = ffs(s->raised[i]);
+ int vector = (i * 32) + id - 1;
ffs will set id to correspond to the first raised interrupt whether
its enabled on not. So in the case where a higher priority interrupt
is masked and both it and a lower priority interrupt occurs, the
vector logic below will be calcalated for the masked higher priority.
Do you really intend to take the vector for a disabled interrupt?
+ if (s->raised[i] & s->fiq[i]) {
+ fiq++;
+ s->reg[ICOLL_STAT] = vector;
+ break;
+ }
+ if (s->raised[i] & s->irq[i]) {
+ irq++;
+ s->reg[ICOLL_STAT] = vector;
So this feels strange. A set IRQ always takes priority over FIQ WRT the vector.
+ break;
+ }
+ }
+ qemu_set_irq(s->parent_irq, irq != 0);
+ qemu_set_irq(s->parent_fiq, fiq != 0);
+}
+
+static void mxs_icoll_set_irq(void *opaque, int irq, int level)
+{
+ mxs_icoll_state *s = (mxs_icoll_state *) opaque;
+ if (level)
+ s->raised[(irq / 32)] |= 1 << (irq % 32);
+ else
+ s->raised[(irq / 32)] &= ~(1 << (irq % 32));
deposit32:
s->raised[irq / 32] = deposit32(s->raised[irq / 32], irq % 32, 1, !!level);
or something like that.
Although looking at this, your s->raised variables are double-edge
sensitive. If the original interrupt goes down so does the raised bit.
Does this really not have any latching behavior (esp for edge
interrupts)?
The other problem with this is level sens. interrupts. If a level
sensitive interrupt occurs from a device and stays high, your
controller can ack it, yet it shouldn't de-assert the interrupt as the
(level sens.) pin is still high. The code here wont retrigger until
the pin strobes from the originating device.
You may need to separate out the raw input pin state from the pending
interrupt state. Originally I was operating under the assumption that
s->raised is simply the raw pin state [1] ....
+ mxs_icoll_update(s);
+}
+
+static uint64_t mxs_icoll_read(void *opaque, hwaddr offset, unsigned size)
+{
+ mxs_icoll_state *s = (mxs_icoll_state *) opaque;
+
+ switch (offset >> 4) {
+ case 0 ... ICOLL_REG_MAX:
+ return s->reg[offset >> 4];
+ case ICOLL_RAW0 ... ICOLL_RAW3:
+ return s->raised[(offset >> 4) - ICOLL_RAW0];
+ case ICOLL_INT0 ... ICOLL_INT127:
+ return s->r[(offset >> 4) - ICOLL_INT0];
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ return 0;
+}
+
+static void mxs_icoll_write(
+ void *opaque, hwaddr offset, uint64_t value, unsigned size)
+{
+ mxs_icoll_state *s = (mxs_icoll_state *) opaque;
+ uint32_t irqval, irqi = 0;
+ uint32_t * dst = NULL;
+ uint32_t oldvalue = 0;
+
+ switch (offset >> 4) {
+ case 0 ... ICOLL_REG_MAX:
+ dst = s->reg + (offset >> 4);
+ break;
+ case ICOLL_INT0 ... ICOLL_INT127:
+ irqi = (offset >> 4) - ICOLL_INT0;
+ irqval = s->r[irqi];
+ dst = &irqval;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ if (!dst) {
+ return;
+ }
+ oldvalue = mxs_write(dst, offset, value, size);
+
+ switch (offset >> 4) {
+ case ICOLL_CTRL:
+ if ((oldvalue ^ s->r[ICOLL_CTRL]) == 0x80000000
+ && !(oldvalue & 0x80000000)) {
+ // printf("%s reseting, anding clockgate\n", __func__);
"resetting"
+ s->r[ICOLL_CTRL] |= 0x40000000;
+ }
+ break;
+ case ICOLL_LEVELACK:
+ irqi = s->reg[ICOLL_STAT] & 0x7f;
+ s->raised[(irqi / 32)] &= ~(1 << (irqi % 32));
[1] ... but this code here is clearing s->raised on software command.
So s->raised seems to have a mixed definition between "raw pin state"
and "interrupt is pending". Perhaps you can clarify what is the
intended meaning of raised?
+ s->reg[ICOLL_STAT] = 0x7f;
+ break;
+ case ICOLL_INT0 ... ICOLL_INT127:
+ s->r[irqi] = irqval & ~(0x40); // dont' set softirq bit
+ if (irqval & 0x4) // ENABLE
+ s->irq[irqi / 32] |= (1 << (irqi % 32));
+ else
+ s->irq[irqi / 32] &= ~(1 << (irqi % 32));
deposit32 can make short work of this.
Regards,
Peter
+ if (irqval & 0x10) // ENFIQ
+ s->fiq[irqi / 32] |= (1 << (irqi % 32));
+ else
+ s->fiq[irqi / 32] &= ~(1 << (irqi % 32));
+ if (irqval & 0x8) // SOFTIRQ
+ mxs_icoll_set_irq(s, irqi, 1);
+ break;
+ }
+
+ mxs_icoll_update(s);
+}
+
+static const MemoryRegionOps mxs_icoll_ops = {
+ .read = mxs_icoll_read,
+ .write = mxs_icoll_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int mxs_icoll_init(SysBusDevice *dev)
+{
+ mxs_icoll_state *s = OBJECT_CHECK(mxs_icoll_state, dev, "mxs_icoll");
+ DeviceState *qdev = DEVICE(dev);
+
+ qdev_init_gpio_in(qdev, mxs_icoll_set_irq, 128);
+ sysbus_init_irq(dev, &s->parent_irq);
+ sysbus_init_irq(dev, &s->parent_fiq);
+ memory_region_init_io(&s->iomem, OBJECT(s), &mxs_icoll_ops, s,
+ "mxs_icoll", 0x2000);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+static void mxs_icoll_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = mxs_icoll_init;
+}
+
+static TypeInfo icoll_info = {
+ .name = "mxs_icoll",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mxs_icoll_state),
+ .class_init = mxs_icoll_class_init,
+};
+
+static void mxs_icoll_register(void)
+{
+ type_register_static(&icoll_info);
+}
+
+type_init(mxs_icoll_register)
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 06/13] mxs/imx23: Add digctl driver
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
` (4 preceding siblings ...)
2013-12-11 13:56 ` [Qemu-devel] [PATCH 05/13] mxs/imx23: Add the interrupt collector Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2014-01-06 15:46 ` Peter Maydell
2014-01-11 8:39 ` Peter Crosthwaite
2013-12-11 13:56 ` [Qemu-devel] [PATCH 07/13] mxs/imx23: Implements the pin mux, GPIOs Michel Pollet
` (7 subsequent siblings)
13 siblings, 2 replies; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
This implements just enough of the digctl IO block to allow
linux to believe it's running on (currently only) an imx23.
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/arm/Makefile.objs | 1 +
hw/arm/imx23_digctl.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 111 insertions(+)
create mode 100644 hw/arm/imx23_digctl.c
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 78b5614..9adcb96 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -5,3 +5,4 @@ obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
obj-y += omap1.o omap2.o strongarm.o
+obj-$(CONFIG_MXS) += imx23_digctl.o
diff --git a/hw/arm/imx23_digctl.c b/hw/arm/imx23_digctl.c
new file mode 100644
index 0000000..b7cd1ff
--- /dev/null
+++ b/hw/arm/imx23_digctl.c
@@ -0,0 +1,110 @@
+/*
+ * imx23_digctl.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ */
+
+/*
+ * This module implements a very basic IO block for the digctl of the imx23
+ * Basically there is no real logic, just constant registers return, the most
+ * used one bing the "chip id" that is used by the various linux drivers
+ * to differentiate between imx23 and 28.
+ *
+ * The module consists mostly of read/write registers that the bootloader and
+ * kernel are quite happy to 'set' to whatever value they believe they set...
+ */
+
+#include "hw/sysbus.h"
+#include "hw/arm/mxs.h"
+
+enum {
+ HW_DIGCTL_RAMCTL = 0x3,
+ HW_DIGCTL_CHIPID = 0x31,
+};
+
+typedef struct imx23_digctl_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t reg[0x2000 / 4];
+} imx23_digctl_state;
+
+static uint64_t imx23_digctl_read(
+ void *opaque, hwaddr offset, unsigned size)
+{
+ imx23_digctl_state *s = (imx23_digctl_state *)opaque;
+ uint32_t res = 0;
+
+ switch (offset >> 4) {
+ case 0 ... 0x2000/4:
+ res = s->reg[offset >> 4];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int)offset);
+ return 0;
+ }
+ return res;
+}
+
+static void imx23_digctl_write(
+ void *opaque, hwaddr offset, uint64_t value, unsigned size)
+{
+ imx23_digctl_state *s = (imx23_digctl_state *) opaque;
+ uint32_t * dst = NULL;
+
+ switch (offset >> 4) {
+ case 0 ... 0x2000 / 4:
+ dst = &s->reg[(offset >> 4)];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ return;
+ }
+ if (!dst) {
+ return;
+ }
+ mxs_write(dst, offset, value, size);
+}
+
+static const MemoryRegionOps imx23_digctl_ops = {
+ .read = imx23_digctl_read,
+ .write = imx23_digctl_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int imx23_digctl_init(SysBusDevice *dev)
+{
+ imx23_digctl_state *s = OBJECT_CHECK(imx23_digctl_state, dev, "imx23_digctl");
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &imx23_digctl_ops, s,
+ "imx23_digctl", 0x2000);
+ sysbus_init_mmio(dev, &s->iomem);
+ s->reg[HW_DIGCTL_RAMCTL] = 0x6d676953; /* default reset value */
+ s->reg[HW_DIGCTL_CHIPID] = 0x37800000; /* i.mX233 */
+ return 0;
+}
+
+static void imx23_digctl_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = imx23_digctl_init;
+}
+
+static TypeInfo digctl_info = {
+ .name = "imx23_digctl",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(imx23_digctl_state),
+ .class_init = imx23_digctl_class_init,
+};
+
+static void imx23_digctl_register(void)
+{
+ type_register_static(&digctl_info);
+}
+
+type_init(imx23_digctl_register)
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 06/13] mxs/imx23: Add digctl driver
2013-12-11 13:56 ` [Qemu-devel] [PATCH 06/13] mxs/imx23: Add digctl driver Michel Pollet
@ 2014-01-06 15:46 ` Peter Maydell
2014-01-08 18:39 ` M P
2014-01-11 8:39 ` Peter Crosthwaite
1 sibling, 1 reply; 36+ messages in thread
From: Peter Maydell @ 2014-01-06 15:46 UTC (permalink / raw)
To: Michel Pollet; +Cc: QEMU Developers
On 11 December 2013 13:56, Michel Pollet <buserror@gmail.com> wrote:
> This implements just enough of the digctl IO block to allow
> linux to believe it's running on (currently only) an imx23.
>
> Signed-off-by: Michel Pollet <buserror@gmail.com>
> ---
> hw/arm/Makefile.objs | 1 +
> hw/arm/imx23_digctl.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 111 insertions(+)
> create mode 100644 hw/arm/imx23_digctl.c
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 78b5614..9adcb96 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -5,3 +5,4 @@ obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
>
> obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
> obj-y += omap1.o omap2.o strongarm.o
> +obj-$(CONFIG_MXS) += imx23_digctl.o
> diff --git a/hw/arm/imx23_digctl.c b/hw/arm/imx23_digctl.c
> new file mode 100644
> index 0000000..b7cd1ff
> --- /dev/null
> +++ b/hw/arm/imx23_digctl.c
> @@ -0,0 +1,110 @@
> +/*
> + * imx23_digctl.c
> + *
> + * Copyright: Michel Pollet <buserror@gmail.com>
> + *
> + * QEMU Licence
> + */
> +
> +/*
> + * This module implements a very basic IO block for the digctl of the imx23
> + * Basically there is no real logic, just constant registers return, the most
> + * used one bing the "chip id" that is used by the various linux drivers
> + * to differentiate between imx23 and 28.
> + *
> + * The module consists mostly of read/write registers that the bootloader and
> + * kernel are quite happy to 'set' to whatever value they believe they set...
> + */
> +
> +#include "hw/sysbus.h"
> +#include "hw/arm/mxs.h"
> +
> +enum {
> + HW_DIGCTL_RAMCTL = 0x3,
> + HW_DIGCTL_CHIPID = 0x31,
> +};
> +
> +typedef struct imx23_digctl_state {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> +
> + uint32_t reg[0x2000 / 4];
> +} imx23_digctl_state;
I'm not generally a fan of "big reg[] array" like this.
In real hardware are these typically constant read/only
registers, or do they actually do something? Does the
hardware really have a full set of 2048 registers here,
or are there gaps?
I'd rather have us implement just the minimal set
required for things to boot, with LOG_UNIMP (and
read-zero/write-ignored) for the rest. That makes
it easier to add actual implementations later
(and your migration state is not 0x2000 bytes of random
undifferentiated stuff).
thanks
-- PMM
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 06/13] mxs/imx23: Add digctl driver
2014-01-06 15:46 ` Peter Maydell
@ 2014-01-08 18:39 ` M P
2014-01-08 18:55 ` Peter Maydell
0 siblings, 1 reply; 36+ messages in thread
From: M P @ 2014-01-08 18:39 UTC (permalink / raw)
To: Peter Maydell; +Cc: QEMU Developers
[-- Attachment #1: Type: text/plain, Size: 3111 bytes --]
On 6 January 2014 15:46, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 11 December 2013 13:56, Michel Pollet <buserror@gmail.com> wrote:
>
> This implements just enough of the digctl IO block to allow
> > linux to believe it's running on (currently only) an imx23.
> >
> > Signed-off-by: Michel Pollet <buserror@gmail.com>
> > ---
> > hw/arm/Makefile.objs | 1 +
> > hw/arm/imx23_digctl.c | 110
> ++++++++++++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 111 insertions(+)
> > create mode 100644 hw/arm/imx23_digctl.c
> >
> > diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> > index 78b5614..9adcb96 100644
> > --- a/hw/arm/Makefile.objs
> > +++ b/hw/arm/Makefile.objs
> > @@ -5,3 +5,4 @@ obj-y += tosa.o versatilepb.o vexpress.o virt.o
> xilinx_zynq.o z2.o
> >
> > obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
> > obj-y += omap1.o omap2.o strongarm.o
> > +obj-$(CONFIG_MXS) += imx23_digctl.o
> > diff --git a/hw/arm/imx23_digctl.c b/hw/arm/imx23_digctl.c
> > new file mode 100644
> > index 0000000..b7cd1ff
> > --- /dev/null
> > +++ b/hw/arm/imx23_digctl.c
> > @@ -0,0 +1,110 @@
> > +/*
> > + * imx23_digctl.c
> > + *
> > + * Copyright: Michel Pollet <buserror@gmail.com>
> > + *
> > + * QEMU Licence
> > + */
> > +
> > +/*
> > + * This module implements a very basic IO block for the digctl of the
> imx23
> > + * Basically there is no real logic, just constant registers return,
> the most
> > + * used one bing the "chip id" that is used by the various linux drivers
> > + * to differentiate between imx23 and 28.
> > + *
> > + * The module consists mostly of read/write registers that the
> bootloader and
> > + * kernel are quite happy to 'set' to whatever value they believe they
> set...
> > + */
> > +
> > +#include "hw/sysbus.h"
> > +#include "hw/arm/mxs.h"
> > +
> > +enum {
> > + HW_DIGCTL_RAMCTL = 0x3,
> > + HW_DIGCTL_CHIPID = 0x31,
> > +};
> > +
> > +typedef struct imx23_digctl_state {
> > + SysBusDevice busdev;
> > + MemoryRegion iomem;
> > +
> > + uint32_t reg[0x2000 / 4];
> > +} imx23_digctl_state;
>
> I'm not generally a fan of "big reg[] array" like this.
> In real hardware are these typically constant read/only
> registers, or do they actually do something? Does the
> hardware really have a full set of 2048 registers here,
> or are there gaps?
>
This block contains most of the 'general purpose' registers, ram timing and
all that jazz; there are a lot of write to it at init time, but it's
otherwise mostly ignored. Also, there's very little to do about it
functionally for qemu's purpose.
>
> I'd rather have us implement just the minimal set
> required for things to boot, with LOG_UNIMP (and
> read-zero/write-ignored) for the rest. That makes
> it easier to add actual implementations later
> (and your migration state is not 0x2000 bytes of random
> undifferentiated stuff).
>
>
I will re-add the trace for both write and read and see if I can narrow the
range down; it will be linux specific, tho, that's why I thought a
'catchall' block was more appropriate.
> thanks
> -- PMM
>
Thanks,
M
[-- Attachment #2: Type: text/html, Size: 4593 bytes --]
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 06/13] mxs/imx23: Add digctl driver
2014-01-08 18:39 ` M P
@ 2014-01-08 18:55 ` Peter Maydell
2014-01-11 8:44 ` Peter Crosthwaite
0 siblings, 1 reply; 36+ messages in thread
From: Peter Maydell @ 2014-01-08 18:55 UTC (permalink / raw)
To: M P; +Cc: QEMU Developers
On 8 January 2014 18:39, M P <buserror@gmail.com> wrote:
> I will re-add the trace for both write and read and see if I can narrow the
> range down; it will be linux specific, tho, that's why I thought a
> 'catchall' block was more appropriate.
Well, we should be implementing what the hardware does,
generally. Misimplementing things as "read as written"
isn't really any better than misimplementing them as
RAZ/WI, it's just differently wrong.
thanks
-- PMM
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 06/13] mxs/imx23: Add digctl driver
2014-01-08 18:55 ` Peter Maydell
@ 2014-01-11 8:44 ` Peter Crosthwaite
0 siblings, 0 replies; 36+ messages in thread
From: Peter Crosthwaite @ 2014-01-11 8:44 UTC (permalink / raw)
To: Peter Maydell; +Cc: M P, QEMU Developers
On Thu, Jan 9, 2014 at 4:55 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 8 January 2014 18:39, M P <buserror@gmail.com> wrote:
>> I will re-add the trace for both write and read and see if I can narrow the
>> range down; it will be linux specific, tho, that's why I thought a
>> 'catchall' block was more appropriate.
I would suggest you do the ones you care about properly, and
RAZ/WI+qemu_log_mask(LOG_UNIMP, the rest. When it comes to the
unimplemented, hard obvious failure from a totally unresponsive
register is preferrable to subtle infidelities (like being able to
write to RO register).
Regards,
Peter
>
> Well, we should be implementing what the hardware does,
> generally. Misimplementing things as "read as written"
> isn't really any better than misimplementing them as
> RAZ/WI, it's just differently wrong.
>
> thanks
> -- PMM
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 06/13] mxs/imx23: Add digctl driver
2013-12-11 13:56 ` [Qemu-devel] [PATCH 06/13] mxs/imx23: Add digctl driver Michel Pollet
2014-01-06 15:46 ` Peter Maydell
@ 2014-01-11 8:39 ` Peter Crosthwaite
1 sibling, 0 replies; 36+ messages in thread
From: Peter Crosthwaite @ 2014-01-11 8:39 UTC (permalink / raw)
To: Michel Pollet; +Cc: qemu-devel@nongnu.org Developers
On Wed, Dec 11, 2013 at 11:56 PM, Michel Pollet <buserror@gmail.com> wrote:
> This implements just enough of the digctl IO block to allow
> linux to believe it's running on (currently only) an imx23.
>
> Signed-off-by: Michel Pollet <buserror@gmail.com>
> ---
> hw/arm/Makefile.objs | 1 +
> hw/arm/imx23_digctl.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 111 insertions(+)
> create mode 100644 hw/arm/imx23_digctl.c
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 78b5614..9adcb96 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -5,3 +5,4 @@ obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
>
> obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
> obj-y += omap1.o omap2.o strongarm.o
> +obj-$(CONFIG_MXS) += imx23_digctl.o
> diff --git a/hw/arm/imx23_digctl.c b/hw/arm/imx23_digctl.c
> new file mode 100644
> index 0000000..b7cd1ff
> --- /dev/null
> +++ b/hw/arm/imx23_digctl.c
> @@ -0,0 +1,110 @@
> +/*
> + * imx23_digctl.c
> + *
> + * Copyright: Michel Pollet <buserror@gmail.com>
> + *
> + * QEMU Licence
> + */
> +
> +/*
> + * This module implements a very basic IO block for the digctl of the imx23
> + * Basically there is no real logic, just constant registers return, the most
> + * used one bing the "chip id" that is used by the various linux drivers
"being", "Linux".
Regards,
Peter
> + * to differentiate between imx23 and 28.
> + *
> + * The module consists mostly of read/write registers that the bootloader and
> + * kernel are quite happy to 'set' to whatever value they believe they set...
> + */
> +
> +#include "hw/sysbus.h"
> +#include "hw/arm/mxs.h"
> +
> +enum {
> + HW_DIGCTL_RAMCTL = 0x3,
> + HW_DIGCTL_CHIPID = 0x31,
> +};
> +
> +typedef struct imx23_digctl_state {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> +
> + uint32_t reg[0x2000 / 4];
> +} imx23_digctl_state;
> +
> +static uint64_t imx23_digctl_read(
> + void *opaque, hwaddr offset, unsigned size)
> +{
> + imx23_digctl_state *s = (imx23_digctl_state *)opaque;
> + uint32_t res = 0;
> +
> + switch (offset >> 4) {
> + case 0 ... 0x2000/4:
> + res = s->reg[offset >> 4];
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int)offset);
> + return 0;
> + }
> + return res;
> +}
> +
> +static void imx23_digctl_write(
> + void *opaque, hwaddr offset, uint64_t value, unsigned size)
> +{
> + imx23_digctl_state *s = (imx23_digctl_state *) opaque;
> + uint32_t * dst = NULL;
> +
> + switch (offset >> 4) {
> + case 0 ... 0x2000 / 4:
> + dst = &s->reg[(offset >> 4)];
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + return;
> + }
> + if (!dst) {
> + return;
> + }
> + mxs_write(dst, offset, value, size);
> +}
> +
> +static const MemoryRegionOps imx23_digctl_ops = {
> + .read = imx23_digctl_read,
> + .write = imx23_digctl_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static int imx23_digctl_init(SysBusDevice *dev)
> +{
> + imx23_digctl_state *s = OBJECT_CHECK(imx23_digctl_state, dev, "imx23_digctl");
> +
> + memory_region_init_io(&s->iomem, OBJECT(s), &imx23_digctl_ops, s,
> + "imx23_digctl", 0x2000);
> + sysbus_init_mmio(dev, &s->iomem);
> + s->reg[HW_DIGCTL_RAMCTL] = 0x6d676953; /* default reset value */
> + s->reg[HW_DIGCTL_CHIPID] = 0x37800000; /* i.mX233 */
Move to rst function.
> + return 0;
> +}
> +
> +static void imx23_digctl_class_init(ObjectClass *klass, void *data)
> +{
> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +
> + sdc->init = imx23_digctl_init;
> +}
> +
> +static TypeInfo digctl_info = {
> + .name = "imx23_digctl",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(imx23_digctl_state),
> + .class_init = imx23_digctl_class_init,
> +};
> +
> +static void imx23_digctl_register(void)
> +{
> + type_register_static(&digctl_info);
> +}
> +
> +type_init(imx23_digctl_register)
> --
> 1.8.5.1
>
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 07/13] mxs/imx23: Implements the pin mux, GPIOs
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
` (5 preceding siblings ...)
2013-12-11 13:56 ` [Qemu-devel] [PATCH 06/13] mxs/imx23: Add digctl driver Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2014-01-06 15:52 ` Peter Maydell
2013-12-11 13:56 ` [Qemu-devel] [PATCH 08/13] mxs/imx23: Add SSP/SPI driver Michel Pollet
` (6 subsequent siblings)
13 siblings, 1 reply; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
Implements the pinctrl and GPIO block for the imx23
It handles GPIO output, and GPIO input from qemu translated
into pin values and interrupts, if appropriate.
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/arm/Makefile.objs | 2 +-
hw/arm/imx23_pinctrl.c | 293 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 294 insertions(+), 1 deletion(-)
create mode 100644 hw/arm/imx23_pinctrl.c
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 9adcb96..ea53988 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -5,4 +5,4 @@ obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
obj-y += omap1.o omap2.o strongarm.o
-obj-$(CONFIG_MXS) += imx23_digctl.o
+obj-$(CONFIG_MXS) += imx23_digctl.o imx23_pinctrl.o
diff --git a/hw/arm/imx23_pinctrl.c b/hw/arm/imx23_pinctrl.c
new file mode 100644
index 0000000..ecfb755
--- /dev/null
+++ b/hw/arm/imx23_pinctrl.c
@@ -0,0 +1,293 @@
+/*
+ * imx23_pinctrl.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ */
+
+/*
+ * Implements the pinctrl and GPIO block for the imx23
+ * It handles GPIO output, and GPIO input from qemu translated
+ * into pin values and interrupts, if appropriate.
+ */
+#include "hw/sysbus.h"
+#include "hw/arm/mxs.h"
+
+#define D(w)
+
+enum {
+ PINCTRL_BANK_COUNT = 3,
+
+ PINCTRL_CTRL = 0,
+ PINCTRL_BANK_MUXSEL = 0x10,
+ PINCTRL_BANK_BASE = 0x40,
+
+ /* these are not << 4 register numbers, these are << 8 register numbers */
+ PINCTRL_BANK_PULL = 0x4,
+ PINCTRL_BANK_OUT = 0x5,
+ PINCTRL_BANK_DIN = 0x6,
+ PINCTRL_BANK_DOE = 0x7,
+ PINCTRL_BANK_PIN2IRQ = 0x8,
+ PINCTRL_BANK_IRQEN = 0x9,
+ PINCTRL_BANK_IRQLEVEL = 0xa,
+ PINCTRL_BANK_IRQPOL = 0xb,
+ PINCTRL_BANK_IRQSTAT = 0xc,
+
+ PINCTRL_BANK_INTERNAL_STATE = 0xd,
+ PINCTRL_MAX = 0xe0,
+};
+
+#define PINCTRL_BANK_REG(_bank, _reg) ((_reg << 8) | (_bank << 4))
+
+enum {
+ MUX_GPIO = 0x3,
+};
+
+
+typedef struct imx23_pinctrl_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t r[PINCTRL_MAX];
+ qemu_irq irq_in[3];
+ qemu_irq irq_out[PINCTRL_BANK_COUNT * 32];
+
+ uint32_t state[PINCTRL_BANK_COUNT];
+} imx23_pinctrl_state;
+
+static uint64_t imx23_pinctrl_read(
+ void *opaque, hwaddr offset, unsigned size)
+{
+ imx23_pinctrl_state *s = (imx23_pinctrl_state *) opaque;
+ uint32_t res = 0;
+
+ switch (offset >> 4) {
+ case 0 ... PINCTRL_MAX:
+ res = s->r[offset >> 4];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+
+ return res;
+}
+
+static uint8_t imx23_pinctrl_getmux(
+ imx23_pinctrl_state *s, int pin)
+{
+ int base = pin / 16, offset = pin % 16;
+ return (s->r[PINCTRL_BANK_MUXSEL + base] >> (offset * 2)) & 0x3;
+}
+
+/*
+ * usage imx23_pinctrl_getbit(s, PINCTRL_BANK_IRQEN, 48)...
+ */
+static uint8_t imx23_pinctrl_getbit(
+ imx23_pinctrl_state *s, uint16_t reg, int pin)
+{
+ int bank = pin / 32, offset = pin % 32;
+ uint32_t * latch = &s->r[PINCTRL_BANK_REG(bank, reg) >> 4];
+// printf("%s bank %d offset %d reg %d : %04x=%08x\n", __func__, bank, offset, reg,
+// PINCTRL_BANK_REG(bank, reg),
+// *latch);
+ return (*latch >> offset) & 0x1;
+}
+
+static void imx23_pinctrl_setbit(
+ imx23_pinctrl_state *s, uint16_t reg, int pin, int value)
+{
+ int bank = pin / 32, offset = pin % 32;
+ uint32_t * latch = &s->r[PINCTRL_BANK_REG(bank, reg) >> 4];
+ *latch = (*latch & ~(1 << offset)) | (!!value << offset);
+}
+
+static void imx23_pinctrl_write_bank(
+ imx23_pinctrl_state *s, int bank,
+ int reg, uint32_t value,
+ uint32_t mask)
+{
+ int set, pin;
+ switch (reg) {
+ /*
+ * Linux has a way of using the DOE&PULL register to toggle the pin
+ */
+ case PINCTRL_BANK_PULL:
+ case PINCTRL_BANK_DOE:
+ /*
+ * Writing to the Data OUT register just triggers the
+ * output qemu IRQ for any further peripherals
+ */
+ case PINCTRL_BANK_OUT: {
+ while ((set = ffs(mask)) > 0) {
+ set--;
+ mask &= ~(1 << set);
+ pin = (bank * 32) + set;
+ /*
+ * For a reason that is not clear, the pullup bit appears
+ * inverted (!) ignoring for now, assume hardware pullup
+ */
+ // imx23_pinctrl_getbit(s, PINCTRL_BANK_PULL, pin)
+ int val =
+ imx23_pinctrl_getbit(s, PINCTRL_BANK_DOE, pin) ?
+ imx23_pinctrl_getbit(s, PINCTRL_BANK_OUT, pin) :
+ 1;
+ D(printf("%s set %2d to OUT:%d DOE:%d (PULL:%d) = %d\n", __func__,
+ pin,
+ imx23_pinctrl_getbit(s, PINCTRL_BANK_OUT, pin),
+ imx23_pinctrl_getbit(s, PINCTRL_BANK_DOE, pin),
+ imx23_pinctrl_getbit(s, PINCTRL_BANK_PULL, pin),
+ val);)
+
+ if (imx23_pinctrl_getbit(s, PINCTRL_BANK_INTERNAL_STATE, pin) != val) {
+ qemu_set_irq(s->irq_out[pin], val);
+ imx23_pinctrl_setbit(s, PINCTRL_BANK_INTERNAL_STATE, pin, val);
+ }
+ }
+ } break;
+ /*
+ * This happends when we receive a qemu IRQ on the input ones,
+ * the register gets updated by the code executed until now,
+ * and all we need to do here is to trigger the imx233 IRQ
+ * if appropriate.
+ * Doing a write to these registers from guest code will acts as
+ * a software interrupt, not entirely sure this is appropriate
+ */
+ case PINCTRL_BANK_DIN: {
+ while ((set = ffs(mask)) > 0) {
+ set--;
+ mask &= ~(1 << set);
+ pin = (bank * 32) + set;
+ D(printf("%s input %2d set to %d\n", __func__,
+ pin, !!(value & (1 << set)));)
+ // its it a GPIO ?
+ if (imx23_pinctrl_getmux(s, pin) != MUX_GPIO) {
+ break;
+ }
+ // if the new value matches the polarity bit, it's the
+ // edge the guy wanted.
+ int valid_irq = ((value >> set) & 1) ==
+ imx23_pinctrl_getbit(s, PINCTRL_BANK_IRQPOL, pin);
+ if (valid_irq) {
+ if (imx23_pinctrl_getbit(s, PINCTRL_BANK_PIN2IRQ, pin)) {
+ imx23_pinctrl_setbit(s, PINCTRL_BANK_IRQSTAT, pin, 1);
+ }
+ // is the interrupt enabled?
+ if (imx23_pinctrl_getbit(s, PINCTRL_BANK_IRQEN, pin)) {
+ qemu_irq_raise(s->irq_in[bank]);
+ }
+ }
+ }
+ } break;
+ }
+}
+
+static void imx23_pinctrl_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ imx23_pinctrl_state *s = (imx23_pinctrl_state *) opaque;
+ uint32_t oldvalue = 0;
+
+// printf("%s offset %04x value %08x\n", __func__, (int)offset, (int)value);
+ switch (offset >> 4) {
+ case 0 ... PINCTRL_MAX:
+ oldvalue = mxs_write(&s->r[offset >> 4], offset, value, size);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ switch (offset >> 4) {
+ case PINCTRL_CTRL:
+ if ((oldvalue ^ s->r[PINCTRL_CTRL]) == 0x80000000
+ && !(oldvalue & 0x80000000)) {
+ // printf("%s reseting, anding clockgate\n", __func__);
+ s->r[PINCTRL_CTRL] |= 0x40000000;
+ }
+ break;
+ case PINCTRL_BANK_BASE ... PINCTRL_MAX: {
+ int bank = (offset >> 4) & 0xf;
+ int reg = (offset >> 8);
+ uint32_t mask = oldvalue ^ s->r[offset >> 4];
+ // printf("%s b %d r %x input %08x mask %08x value %08x\n",
+ // __func__, bank, reg, (int)value, mask, s->r[offset >> 4]);
+ imx23_pinctrl_write_bank(s, bank, reg,
+ s->r[offset >> 4], mask);
+ } break;
+ }
+}
+
+/*
+ * This will interfaces qemu other components back to the guest input pins
+ */
+static void imx23_pinctrl_set_irq(void *opaque, int irq, int level)
+{
+ // simulate a write to the data IN address
+ int bank = irq / 32;
+ hwaddr offset =
+ PINCTRL_BANK_REG(bank, PINCTRL_BANK_DIN);
+ uint64_t value = (1 << (irq % 32));
+ D(printf("%s %d = %d\n", __func__, irq, level);)
+ imx23_pinctrl_write(opaque,
+ offset + (level ? 0x4 : 0x8),
+ value, 4);
+}
+
+static const MemoryRegionOps imx23_pinctrl_ops = {
+ .read = imx23_pinctrl_read,
+ .write = imx23_pinctrl_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int imx23_pinctrl_init(SysBusDevice *dev)
+{
+ imx23_pinctrl_state *s = OBJECT_CHECK(imx23_pinctrl_state, dev, "imx23_pinctrl");
+ int i;
+ DeviceState *qdev = DEVICE(dev);
+
+ // NEEDED for qdev_find_recursive to work
+ qdev->id = "imx23_pinctrl";
+ qdev_init_gpio_in(qdev, imx23_pinctrl_set_irq, 32 * PINCTRL_BANK_COUNT);
+ qdev_init_gpio_out(qdev, s->irq_out, ARRAY_SIZE(s->irq_out));
+ memory_region_init_io(&s->iomem, OBJECT(s), &imx23_pinctrl_ops, s,
+ "imx23_pinctrl", 0x2000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ for (i = 0; i < PINCTRL_BANK_COUNT; i++) {
+ sysbus_init_irq(dev, &s->irq_in[i]);
+ s->r[PINCTRL_BANK_REG(i, PINCTRL_BANK_DIN) >> 4] = 0;
+ s->r[PINCTRL_BANK_REG(i, PINCTRL_BANK_PULL) >> 4] = 0xffffffff;
+ }
+ /* set default mux values */
+ for (i = 0; i < 8; i++)
+ s->r[(0x100 >> 4) + i] = 0x33333333;
+
+ s->r[PINCTRL_CTRL] = 0xcf000000;
+
+ return 0;
+}
+
+
+static void imx23_pinctrl_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = imx23_pinctrl_init;
+}
+
+static TypeInfo pinctrl_info = {
+ .name = "imx23_pinctrl",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(imx23_pinctrl_state),
+ .class_init = imx23_pinctrl_class_init,
+};
+
+static void imx23_pinctrl_register(void)
+{
+ type_register_static(&pinctrl_info);
+}
+
+type_init(imx23_pinctrl_register)
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 07/13] mxs/imx23: Implements the pin mux, GPIOs
2013-12-11 13:56 ` [Qemu-devel] [PATCH 07/13] mxs/imx23: Implements the pin mux, GPIOs Michel Pollet
@ 2014-01-06 15:52 ` Peter Maydell
2014-01-08 18:16 ` M P
0 siblings, 1 reply; 36+ messages in thread
From: Peter Maydell @ 2014-01-06 15:52 UTC (permalink / raw)
To: Michel Pollet; +Cc: QEMU Developers
On 11 December 2013 13:56, Michel Pollet <buserror@gmail.com> wrote:
> Implements the pinctrl and GPIO block for the imx23
> It handles GPIO output, and GPIO input from qemu translated
> into pin values and interrupts, if appropriate.
>
> Signed-off-by: Michel Pollet <buserror@gmail.com>
> ---
> hw/arm/Makefile.objs | 2 +-
> hw/arm/imx23_pinctrl.c | 293 +++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 294 insertions(+), 1 deletion(-)
> create mode 100644 hw/arm/imx23_pinctrl.c
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 9adcb96..ea53988 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -5,4 +5,4 @@ obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
>
> obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
> obj-y += omap1.o omap2.o strongarm.o
> -obj-$(CONFIG_MXS) += imx23_digctl.o
> +obj-$(CONFIG_MXS) += imx23_digctl.o imx23_pinctrl.o
> diff --git a/hw/arm/imx23_pinctrl.c b/hw/arm/imx23_pinctrl.c
> new file mode 100644
> index 0000000..ecfb755
> --- /dev/null
> +++ b/hw/arm/imx23_pinctrl.c
> @@ -0,0 +1,293 @@
> +/*
> + * imx23_pinctrl.c
> + *
> + * Copyright: Michel Pollet <buserror@gmail.com>
> + *
> + * QEMU Licence
> + */
> +
> +/*
> + * Implements the pinctrl and GPIO block for the imx23
> + * It handles GPIO output, and GPIO input from qemu translated
> + * into pin values and interrupts, if appropriate.
> + */
> +#include "hw/sysbus.h"
> +#include "hw/arm/mxs.h"
> +
> +#define D(w)
> +
> +enum {
> + PINCTRL_BANK_COUNT = 3,
> +
> + PINCTRL_CTRL = 0,
> + PINCTRL_BANK_MUXSEL = 0x10,
> + PINCTRL_BANK_BASE = 0x40,
> +
> + /* these are not << 4 register numbers, these are << 8 register numbers */
> + PINCTRL_BANK_PULL = 0x4,
> + PINCTRL_BANK_OUT = 0x5,
> + PINCTRL_BANK_DIN = 0x6,
> + PINCTRL_BANK_DOE = 0x7,
> + PINCTRL_BANK_PIN2IRQ = 0x8,
> + PINCTRL_BANK_IRQEN = 0x9,
> + PINCTRL_BANK_IRQLEVEL = 0xa,
> + PINCTRL_BANK_IRQPOL = 0xb,
> + PINCTRL_BANK_IRQSTAT = 0xc,
> +
> + PINCTRL_BANK_INTERNAL_STATE = 0xd,
> + PINCTRL_MAX = 0xe0,
> +};
> +
> +#define PINCTRL_BANK_REG(_bank, _reg) ((_reg << 8) | (_bank << 4))
> +
> +enum {
> + MUX_GPIO = 0x3,
> +};
> +
> +
> +typedef struct imx23_pinctrl_state {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> +
> + uint32_t r[PINCTRL_MAX];
> + qemu_irq irq_in[3];
> + qemu_irq irq_out[PINCTRL_BANK_COUNT * 32];
> +
> + uint32_t state[PINCTRL_BANK_COUNT];
> +} imx23_pinctrl_state;
> +
> +static uint64_t imx23_pinctrl_read(
> + void *opaque, hwaddr offset, unsigned size)
> +{
> + imx23_pinctrl_state *s = (imx23_pinctrl_state *) opaque;
> + uint32_t res = 0;
> +
> + switch (offset >> 4) {
> + case 0 ... PINCTRL_MAX:
> + res = s->r[offset >> 4];
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> +
> + return res;
> +}
> +
> +static uint8_t imx23_pinctrl_getmux(
> + imx23_pinctrl_state *s, int pin)
> +{
> + int base = pin / 16, offset = pin % 16;
> + return (s->r[PINCTRL_BANK_MUXSEL + base] >> (offset * 2)) & 0x3;
> +}
> +
> +/*
> + * usage imx23_pinctrl_getbit(s, PINCTRL_BANK_IRQEN, 48)...
> + */
> +static uint8_t imx23_pinctrl_getbit(
> + imx23_pinctrl_state *s, uint16_t reg, int pin)
> +{
> + int bank = pin / 32, offset = pin % 32;
> + uint32_t * latch = &s->r[PINCTRL_BANK_REG(bank, reg) >> 4];
> +// printf("%s bank %d offset %d reg %d : %04x=%08x\n", __func__, bank, offset, reg,
> +// PINCTRL_BANK_REG(bank, reg),
> +// *latch);
> + return (*latch >> offset) & 0x1;
> +}
> +
> +static void imx23_pinctrl_setbit(
> + imx23_pinctrl_state *s, uint16_t reg, int pin, int value)
> +{
> + int bank = pin / 32, offset = pin % 32;
> + uint32_t * latch = &s->r[PINCTRL_BANK_REG(bank, reg) >> 4];
> + *latch = (*latch & ~(1 << offset)) | (!!value << offset);
deposit32() will make this clearer to read.
> +}
> +
> +static void imx23_pinctrl_write_bank(
> + imx23_pinctrl_state *s, int bank,
> + int reg, uint32_t value,
> + uint32_t mask)
> +{
> + int set, pin;
> + switch (reg) {
> + /*
> + * Linux has a way of using the DOE&PULL register to toggle the pin
> + */
Why is this comment here? We should ideally not care what
guest OS we run, we should just implement the h/w correctly.
> + case PINCTRL_BANK_PULL:
> + case PINCTRL_BANK_DOE:
> + /*
> + * Writing to the Data OUT register just triggers the
> + * output qemu IRQ for any further peripherals
> + */
> + case PINCTRL_BANK_OUT: {
> + while ((set = ffs(mask)) > 0) {
> + set--;
> + mask &= ~(1 << set);
> + pin = (bank * 32) + set;
> + /*
> + * For a reason that is not clear, the pullup bit appears
> + * inverted (!) ignoring for now, assume hardware pullup
> + */
You should figure out what's actually happening here...
> + // imx23_pinctrl_getbit(s, PINCTRL_BANK_PULL, pin)
> + int val =
> + imx23_pinctrl_getbit(s, PINCTRL_BANK_DOE, pin) ?
> + imx23_pinctrl_getbit(s, PINCTRL_BANK_OUT, pin) :
> + 1;
> + D(printf("%s set %2d to OUT:%d DOE:%d (PULL:%d) = %d\n", __func__,
> + pin,
> + imx23_pinctrl_getbit(s, PINCTRL_BANK_OUT, pin),
> + imx23_pinctrl_getbit(s, PINCTRL_BANK_DOE, pin),
> + imx23_pinctrl_getbit(s, PINCTRL_BANK_PULL, pin),
> + val);)
> +
> + if (imx23_pinctrl_getbit(s, PINCTRL_BANK_INTERNAL_STATE, pin) != val) {
> + qemu_set_irq(s->irq_out[pin], val);
> + imx23_pinctrl_setbit(s, PINCTRL_BANK_INTERNAL_STATE, pin, val);
> + }
> + }
> + } break;
> + /*
> + * This happends when we receive a qemu IRQ on the input ones,
> + * the register gets updated by the code executed until now,
> + * and all we need to do here is to trigger the imx233 IRQ
> + * if appropriate.
> + * Doing a write to these registers from guest code will acts as
> + * a software interrupt, not entirely sure this is appropriate
> + */
> + case PINCTRL_BANK_DIN: {
> + while ((set = ffs(mask)) > 0) {
> + set--;
> + mask &= ~(1 << set);
> + pin = (bank * 32) + set;
> + D(printf("%s input %2d set to %d\n", __func__,
> + pin, !!(value & (1 << set)));)
> + // its it a GPIO ?
> + if (imx23_pinctrl_getmux(s, pin) != MUX_GPIO) {
> + break;
> + }
> + // if the new value matches the polarity bit, it's the
> + // edge the guy wanted.
> + int valid_irq = ((value >> set) & 1) ==
> + imx23_pinctrl_getbit(s, PINCTRL_BANK_IRQPOL, pin);
> + if (valid_irq) {
> + if (imx23_pinctrl_getbit(s, PINCTRL_BANK_PIN2IRQ, pin)) {
> + imx23_pinctrl_setbit(s, PINCTRL_BANK_IRQSTAT, pin, 1);
> + }
> + // is the interrupt enabled?
> + if (imx23_pinctrl_getbit(s, PINCTRL_BANK_IRQEN, pin)) {
> + qemu_irq_raise(s->irq_in[bank]);
> + }
> + }
> + }
> + } break;
> + }
> +}
> +
> +static void imx23_pinctrl_write(void *opaque, hwaddr offset,
> + uint64_t value, unsigned size)
> +{
> + imx23_pinctrl_state *s = (imx23_pinctrl_state *) opaque;
> + uint32_t oldvalue = 0;
> +
> +// printf("%s offset %04x value %08x\n", __func__, (int)offset, (int)value);
All this commented out debug code needs to go.
> + switch (offset >> 4) {
> + case 0 ... PINCTRL_MAX:
> + oldvalue = mxs_write(&s->r[offset >> 4], offset, value, size);
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> + switch (offset >> 4) {
> + case PINCTRL_CTRL:
> + if ((oldvalue ^ s->r[PINCTRL_CTRL]) == 0x80000000
> + && !(oldvalue & 0x80000000)) {
> + // printf("%s reseting, anding clockgate\n", __func__);
> + s->r[PINCTRL_CTRL] |= 0x40000000;
> + }
> + break;
> + case PINCTRL_BANK_BASE ... PINCTRL_MAX: {
> + int bank = (offset >> 4) & 0xf;
> + int reg = (offset >> 8);
> + uint32_t mask = oldvalue ^ s->r[offset >> 4];
> + // printf("%s b %d r %x input %08x mask %08x value %08x\n",
> + // __func__, bank, reg, (int)value, mask, s->r[offset >> 4]);
> + imx23_pinctrl_write_bank(s, bank, reg,
> + s->r[offset >> 4], mask);
> + } break;
> + }
> +}
> +
> +/*
> + * This will interfaces qemu other components back to the guest input pins
> + */
> +static void imx23_pinctrl_set_irq(void *opaque, int irq, int level)
> +{
> + // simulate a write to the data IN address
> + int bank = irq / 32;
> + hwaddr offset =
> + PINCTRL_BANK_REG(bank, PINCTRL_BANK_DIN);
> + uint64_t value = (1 << (irq % 32));
> + D(printf("%s %d = %d\n", __func__, irq, level);)
> + imx23_pinctrl_write(opaque,
> + offset + (level ? 0x4 : 0x8),
> + value, 4);
> +}
> +
> +static const MemoryRegionOps imx23_pinctrl_ops = {
> + .read = imx23_pinctrl_read,
> + .write = imx23_pinctrl_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static int imx23_pinctrl_init(SysBusDevice *dev)
> +{
> + imx23_pinctrl_state *s = OBJECT_CHECK(imx23_pinctrl_state, dev, "imx23_pinctrl");
> + int i;
> + DeviceState *qdev = DEVICE(dev);
> +
> + // NEEDED for qdev_find_recursive to work
> + qdev->id = "imx23_pinctrl";
This looks suspicious. Nobody else needs to set qdev->id,
why is your device special?
> + qdev_init_gpio_in(qdev, imx23_pinctrl_set_irq, 32 * PINCTRL_BANK_COUNT);
> + qdev_init_gpio_out(qdev, s->irq_out, ARRAY_SIZE(s->irq_out));
> + memory_region_init_io(&s->iomem, OBJECT(s), &imx23_pinctrl_ops, s,
> + "imx23_pinctrl", 0x2000);
> + sysbus_init_mmio(dev, &s->iomem);
> +
> + for (i = 0; i < PINCTRL_BANK_COUNT; i++) {
> + sysbus_init_irq(dev, &s->irq_in[i]);
> + s->r[PINCTRL_BANK_REG(i, PINCTRL_BANK_DIN) >> 4] = 0;
> + s->r[PINCTRL_BANK_REG(i, PINCTRL_BANK_PULL) >> 4] = 0xffffffff;
> + }
> + /* set default mux values */
> + for (i = 0; i < 8; i++)
> + s->r[(0x100 >> 4) + i] = 0x33333333;
> +
> + s->r[PINCTRL_CTRL] = 0xcf000000;
> +
> + return 0;
> +}
> +
> +
> +static void imx23_pinctrl_class_init(ObjectClass *klass, void *data)
> +{
> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +
> + sdc->init = imx23_pinctrl_init;
> +}
> +
> +static TypeInfo pinctrl_info = {
> + .name = "imx23_pinctrl",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(imx23_pinctrl_state),
> + .class_init = imx23_pinctrl_class_init,
> +};
> +
> +static void imx23_pinctrl_register(void)
> +{
> + type_register_static(&pinctrl_info);
> +}
> +
> +type_init(imx23_pinctrl_register)
> --
> 1.8.5.1
I stopped reviewing at this patch; you should probably
start by fixing the points I mentioned for all your
patches, and then post a v2 series.
thanks
-- PMM
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 07/13] mxs/imx23: Implements the pin mux, GPIOs
2014-01-06 15:52 ` Peter Maydell
@ 2014-01-08 18:16 ` M P
0 siblings, 0 replies; 36+ messages in thread
From: M P @ 2014-01-08 18:16 UTC (permalink / raw)
To: Peter Maydell; +Cc: QEMU Developers
[-- Attachment #1: Type: text/plain, Size: 12861 bytes --]
All noted, and thanks for all the bits you reviewed so far, I'll do the
changes and resubmit.
M
On 6 January 2014 15:52, Peter Maydell <peter.maydell@linaro.org> wrote:
> On 11 December 2013 13:56, Michel Pollet <buserror@gmail.com> wrote:
> > Implements the pinctrl and GPIO block for the imx23
> > It handles GPIO output, and GPIO input from qemu translated
> > into pin values and interrupts, if appropriate.
> >
> > Signed-off-by: Michel Pollet <buserror@gmail.com>
> > ---
> > hw/arm/Makefile.objs | 2 +-
> > hw/arm/imx23_pinctrl.c | 293
> +++++++++++++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 294 insertions(+), 1 deletion(-)
> > create mode 100644 hw/arm/imx23_pinctrl.c
> >
> > diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> > index 9adcb96..ea53988 100644
> > --- a/hw/arm/Makefile.objs
> > +++ b/hw/arm/Makefile.objs
> > @@ -5,4 +5,4 @@ obj-y += tosa.o versatilepb.o vexpress.o virt.o
> xilinx_zynq.o z2.o
> >
> > obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
> > obj-y += omap1.o omap2.o strongarm.o
> > -obj-$(CONFIG_MXS) += imx23_digctl.o
> > +obj-$(CONFIG_MXS) += imx23_digctl.o imx23_pinctrl.o
> > diff --git a/hw/arm/imx23_pinctrl.c b/hw/arm/imx23_pinctrl.c
> > new file mode 100644
> > index 0000000..ecfb755
> > --- /dev/null
> > +++ b/hw/arm/imx23_pinctrl.c
> > @@ -0,0 +1,293 @@
> > +/*
> > + * imx23_pinctrl.c
> > + *
> > + * Copyright: Michel Pollet <buserror@gmail.com>
> > + *
> > + * QEMU Licence
> > + */
> > +
> > +/*
> > + * Implements the pinctrl and GPIO block for the imx23
> > + * It handles GPIO output, and GPIO input from qemu translated
> > + * into pin values and interrupts, if appropriate.
> > + */
> > +#include "hw/sysbus.h"
> > +#include "hw/arm/mxs.h"
> > +
> > +#define D(w)
> > +
> > +enum {
> > + PINCTRL_BANK_COUNT = 3,
> > +
> > + PINCTRL_CTRL = 0,
> > + PINCTRL_BANK_MUXSEL = 0x10,
> > + PINCTRL_BANK_BASE = 0x40,
> > +
> > + /* these are not << 4 register numbers, these are << 8 register
> numbers */
> > + PINCTRL_BANK_PULL = 0x4,
> > + PINCTRL_BANK_OUT = 0x5,
> > + PINCTRL_BANK_DIN = 0x6,
> > + PINCTRL_BANK_DOE = 0x7,
> > + PINCTRL_BANK_PIN2IRQ = 0x8,
> > + PINCTRL_BANK_IRQEN = 0x9,
> > + PINCTRL_BANK_IRQLEVEL = 0xa,
> > + PINCTRL_BANK_IRQPOL = 0xb,
> > + PINCTRL_BANK_IRQSTAT = 0xc,
> > +
> > + PINCTRL_BANK_INTERNAL_STATE = 0xd,
> > + PINCTRL_MAX = 0xe0,
> > +};
> > +
> > +#define PINCTRL_BANK_REG(_bank, _reg) ((_reg << 8) | (_bank << 4))
> > +
> > +enum {
> > + MUX_GPIO = 0x3,
> > +};
> > +
> > +
> > +typedef struct imx23_pinctrl_state {
> > + SysBusDevice busdev;
> > + MemoryRegion iomem;
> > +
> > + uint32_t r[PINCTRL_MAX];
> > + qemu_irq irq_in[3];
> > + qemu_irq irq_out[PINCTRL_BANK_COUNT * 32];
> > +
> > + uint32_t state[PINCTRL_BANK_COUNT];
> > +} imx23_pinctrl_state;
> > +
> > +static uint64_t imx23_pinctrl_read(
> > + void *opaque, hwaddr offset, unsigned size)
> > +{
> > + imx23_pinctrl_state *s = (imx23_pinctrl_state *) opaque;
> > + uint32_t res = 0;
> > +
> > + switch (offset >> 4) {
> > + case 0 ... PINCTRL_MAX:
> > + res = s->r[offset >> 4];
> > + break;
> > + default:
> > + qemu_log_mask(LOG_GUEST_ERROR,
> > + "%s: bad offset 0x%x\n", __func__, (int) offset);
> > + break;
> > + }
> > +
> > + return res;
> > +}
> > +
> > +static uint8_t imx23_pinctrl_getmux(
> > + imx23_pinctrl_state *s, int pin)
> > +{
> > + int base = pin / 16, offset = pin % 16;
> > + return (s->r[PINCTRL_BANK_MUXSEL + base] >> (offset * 2)) & 0x3;
> > +}
> > +
> > +/*
> > + * usage imx23_pinctrl_getbit(s, PINCTRL_BANK_IRQEN, 48)...
> > + */
> > +static uint8_t imx23_pinctrl_getbit(
> > + imx23_pinctrl_state *s, uint16_t reg, int pin)
> > +{
> > + int bank = pin / 32, offset = pin % 32;
> > + uint32_t * latch = &s->r[PINCTRL_BANK_REG(bank, reg) >> 4];
> > +// printf("%s bank %d offset %d reg %d : %04x=%08x\n", __func__,
> bank, offset, reg,
> > +// PINCTRL_BANK_REG(bank, reg),
> > +// *latch);
> > + return (*latch >> offset) & 0x1;
> > +}
> > +
> > +static void imx23_pinctrl_setbit(
> > + imx23_pinctrl_state *s, uint16_t reg, int pin, int value)
> > +{
> > + int bank = pin / 32, offset = pin % 32;
> > + uint32_t * latch = &s->r[PINCTRL_BANK_REG(bank, reg) >> 4];
> > + *latch = (*latch & ~(1 << offset)) | (!!value << offset);
>
> deposit32() will make this clearer to read.
>
> > +}
> > +
> > +static void imx23_pinctrl_write_bank(
> > + imx23_pinctrl_state *s, int bank,
> > + int reg, uint32_t value,
> > + uint32_t mask)
> > +{
> > + int set, pin;
> > + switch (reg) {
> > + /*
> > + * Linux has a way of using the DOE&PULL register to toggle the
> pin
> > + */
>
> Why is this comment here? We should ideally not care what
> guest OS we run, we should just implement the h/w correctly.
>
> > + case PINCTRL_BANK_PULL:
> > + case PINCTRL_BANK_DOE:
> > + /*
> > + * Writing to the Data OUT register just triggers the
> > + * output qemu IRQ for any further peripherals
> > + */
> > + case PINCTRL_BANK_OUT: {
> > + while ((set = ffs(mask)) > 0) {
> > + set--;
> > + mask &= ~(1 << set);
> > + pin = (bank * 32) + set;
> > + /*
> > + * For a reason that is not clear, the pullup bit
> appears
> > + * inverted (!) ignoring for now, assume hardware pullup
> > + */
>
> You should figure out what's actually happening here...
>
> > + // imx23_pinctrl_getbit(s, PINCTRL_BANK_PULL, pin)
> > + int val =
> > + imx23_pinctrl_getbit(s, PINCTRL_BANK_DOE, pin) ?
> > + imx23_pinctrl_getbit(s,
> PINCTRL_BANK_OUT, pin) :
> > + 1;
> > + D(printf("%s set %2d to OUT:%d DOE:%d (PULL:%d) =
> %d\n", __func__,
> > + pin,
> > + imx23_pinctrl_getbit(s, PINCTRL_BANK_OUT, pin),
> > + imx23_pinctrl_getbit(s, PINCTRL_BANK_DOE, pin),
> > + imx23_pinctrl_getbit(s, PINCTRL_BANK_PULL, pin),
> > + val);)
> > +
> > + if (imx23_pinctrl_getbit(s,
> PINCTRL_BANK_INTERNAL_STATE, pin) != val) {
> > + qemu_set_irq(s->irq_out[pin], val);
> > + imx23_pinctrl_setbit(s,
> PINCTRL_BANK_INTERNAL_STATE, pin, val);
> > + }
> > + }
> > + } break;
> > + /*
> > + * This happends when we receive a qemu IRQ on the input ones,
> > + * the register gets updated by the code executed until now,
> > + * and all we need to do here is to trigger the imx233 IRQ
> > + * if appropriate.
> > + * Doing a write to these registers from guest code will acts as
> > + * a software interrupt, not entirely sure this is appropriate
> > + */
> > + case PINCTRL_BANK_DIN: {
> > + while ((set = ffs(mask)) > 0) {
> > + set--;
> > + mask &= ~(1 << set);
> > + pin = (bank * 32) + set;
> > + D(printf("%s input %2d set to %d\n", __func__,
> > + pin, !!(value & (1 << set)));)
> > + // its it a GPIO ?
> > + if (imx23_pinctrl_getmux(s, pin) != MUX_GPIO) {
> > + break;
> > + }
> > + // if the new value matches the polarity bit, it's the
> > + // edge the guy wanted.
> > + int valid_irq = ((value >> set) & 1) ==
> > + imx23_pinctrl_getbit(s, PINCTRL_BANK_IRQPOL,
> pin);
> > + if (valid_irq) {
> > + if (imx23_pinctrl_getbit(s, PINCTRL_BANK_PIN2IRQ,
> pin)) {
> > + imx23_pinctrl_setbit(s, PINCTRL_BANK_IRQSTAT,
> pin, 1);
> > + }
> > + // is the interrupt enabled?
> > + if (imx23_pinctrl_getbit(s, PINCTRL_BANK_IRQEN,
> pin)) {
> > + qemu_irq_raise(s->irq_in[bank]);
> > + }
> > + }
> > + }
> > + } break;
> > + }
> > +}
> > +
> > +static void imx23_pinctrl_write(void *opaque, hwaddr offset,
> > + uint64_t value, unsigned size)
> > +{
> > + imx23_pinctrl_state *s = (imx23_pinctrl_state *) opaque;
> > + uint32_t oldvalue = 0;
> > +
> > +// printf("%s offset %04x value %08x\n", __func__, (int)offset,
> (int)value);
>
> All this commented out debug code needs to go.
>
> > + switch (offset >> 4) {
> > + case 0 ... PINCTRL_MAX:
> > + oldvalue = mxs_write(&s->r[offset >> 4], offset, value,
> size);
> > + break;
> > + default:
> > + qemu_log_mask(LOG_GUEST_ERROR,
> > + "%s: bad offset 0x%x\n", __func__, (int) offset);
> > + break;
> > + }
> > + switch (offset >> 4) {
> > + case PINCTRL_CTRL:
> > + if ((oldvalue ^ s->r[PINCTRL_CTRL]) == 0x80000000
> > + && !(oldvalue & 0x80000000)) {
> > + // printf("%s reseting, anding clockgate\n", __func__);
> > + s->r[PINCTRL_CTRL] |= 0x40000000;
> > + }
> > + break;
> > + case PINCTRL_BANK_BASE ... PINCTRL_MAX: {
> > + int bank = (offset >> 4) & 0xf;
> > + int reg = (offset >> 8);
> > + uint32_t mask = oldvalue ^ s->r[offset >> 4];
> > + // printf("%s b %d r %x input %08x mask %08x value %08x\n",
> > + // __func__, bank, reg, (int)value, mask,
> s->r[offset >> 4]);
> > + imx23_pinctrl_write_bank(s, bank, reg,
> > + s->r[offset >> 4], mask);
> > + } break;
> > + }
> > +}
> > +
> > +/*
> > + * This will interfaces qemu other components back to the guest input
> pins
> > + */
> > +static void imx23_pinctrl_set_irq(void *opaque, int irq, int level)
> > +{
> > + // simulate a write to the data IN address
> > + int bank = irq / 32;
> > + hwaddr offset =
> > + PINCTRL_BANK_REG(bank, PINCTRL_BANK_DIN);
> > + uint64_t value = (1 << (irq % 32));
> > + D(printf("%s %d = %d\n", __func__, irq, level);)
> > + imx23_pinctrl_write(opaque,
> > + offset + (level ? 0x4 : 0x8),
> > + value, 4);
> > +}
> > +
> > +static const MemoryRegionOps imx23_pinctrl_ops = {
> > + .read = imx23_pinctrl_read,
> > + .write = imx23_pinctrl_write,
> > + .endianness = DEVICE_NATIVE_ENDIAN,
> > +};
> > +
> > +static int imx23_pinctrl_init(SysBusDevice *dev)
> > +{
> > + imx23_pinctrl_state *s = OBJECT_CHECK(imx23_pinctrl_state, dev,
> "imx23_pinctrl");
> > + int i;
> > + DeviceState *qdev = DEVICE(dev);
> > +
> > + // NEEDED for qdev_find_recursive to work
> > + qdev->id = "imx23_pinctrl";
>
> This looks suspicious. Nobody else needs to set qdev->id,
> why is your device special?
>
> > + qdev_init_gpio_in(qdev, imx23_pinctrl_set_irq, 32 *
> PINCTRL_BANK_COUNT);
> > + qdev_init_gpio_out(qdev, s->irq_out, ARRAY_SIZE(s->irq_out));
> > + memory_region_init_io(&s->iomem, OBJECT(s), &imx23_pinctrl_ops, s,
> > + "imx23_pinctrl", 0x2000);
> > + sysbus_init_mmio(dev, &s->iomem);
> > +
> > + for (i = 0; i < PINCTRL_BANK_COUNT; i++) {
> > + sysbus_init_irq(dev, &s->irq_in[i]);
> > + s->r[PINCTRL_BANK_REG(i, PINCTRL_BANK_DIN) >> 4] = 0;
> > + s->r[PINCTRL_BANK_REG(i, PINCTRL_BANK_PULL) >> 4] = 0xffffffff;
> > + }
> > + /* set default mux values */
> > + for (i = 0; i < 8; i++)
> > + s->r[(0x100 >> 4) + i] = 0x33333333;
> > +
> > + s->r[PINCTRL_CTRL] = 0xcf000000;
> > +
> > + return 0;
> > +}
> > +
> > +
> > +static void imx23_pinctrl_class_init(ObjectClass *klass, void *data)
> > +{
> > + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> > +
> > + sdc->init = imx23_pinctrl_init;
> > +}
> > +
> > +static TypeInfo pinctrl_info = {
> > + .name = "imx23_pinctrl",
> > + .parent = TYPE_SYS_BUS_DEVICE,
> > + .instance_size = sizeof(imx23_pinctrl_state),
> > + .class_init = imx23_pinctrl_class_init,
> > +};
> > +
> > +static void imx23_pinctrl_register(void)
> > +{
> > + type_register_static(&pinctrl_info);
> > +}
> > +
> > +type_init(imx23_pinctrl_register)
> > --
> > 1.8.5.1
>
> I stopped reviewing at this patch; you should probably
> start by fixing the points I mentioned for all your
> patches, and then post a v2 series.
>
> thanks
> -- PMM
>
[-- Attachment #2: Type: text/html, Size: 16005 bytes --]
^ permalink raw reply [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 08/13] mxs/imx23: Add SSP/SPI driver
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
` (6 preceding siblings ...)
2013-12-11 13:56 ` [Qemu-devel] [PATCH 07/13] mxs/imx23: Implements the pin mux, GPIOs Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2014-01-11 9:08 ` Peter Crosthwaite
2013-12-11 13:56 ` [Qemu-devel] [PATCH 09/13] mxs/imx23: Add the RTC block Michel Pollet
` (5 subsequent siblings)
13 siblings, 1 reply; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
This implements the SSP port(s) of the mxs. Currently hardcoded for
the SD card interface, but as TODO it could rather easily be made
to be generic and support 'generic' SPI too.
It is geared toward working with DMA, as the linux drivers uses it that
way.
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/ssi/Makefile.objs | 1 +
hw/ssi/mxs_spi.c | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 240 insertions(+)
create mode 100644 hw/ssi/mxs_spi.c
diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
index 9555825..72ec849 100644
--- a/hw/ssi/Makefile.objs
+++ b/hw/ssi/Makefile.objs
@@ -4,3 +4,4 @@ common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
obj-$(CONFIG_OMAP) += omap_spi.o
+obj-$(CONFIG_MXS) += mxs_spi.o
diff --git a/hw/ssi/mxs_spi.c b/hw/ssi/mxs_spi.c
new file mode 100644
index 0000000..8bc70f9
--- /dev/null
+++ b/hw/ssi/mxs_spi.c
@@ -0,0 +1,239 @@
+/*
+ * mxs_ssp.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ */
+
+/*
+ * This implements the SSP port(s) of the mxs. Currently hardcoded for the
+ * SD card interface, but as TODO it could rather easily be made to be generic
+ * and support 'generic' SPI too.
+ * It is geared toward working with DMA, as the linux drivers uses it that way.
+ */
+#include "hw/sysbus.h"
+#include "hw/arm/mxs.h"
+#include "sysemu/blockdev.h"
+#include "hw/sd.h"
+
+/*
+ * SSP register indexes, most of the useful ones
+ */
+enum {
+ SSP_CTRL = 0x0,
+ SSP_SD_CMD0 = 0x1,
+ SSP_SD_CMD1 = 0x2,
+ SSP_COMPREF = 0x3,
+ SSP_COMPMASK = 0x4,
+ SSP_TIMING = 0x5,
+ SSP_CTRL1 = 0x6,
+ SSP_DATA = 0x7,
+ SSP_SDRESP0 = 0x8,
+ SSP_SDRESP1 = 0x9,
+ SSP_SDRESP2 = 0xa,
+ SSP_SDRESP3 = 0xb,
+ SSP_STATUS = 0xc,
+
+ SSP_VERSION = 0x11,
+ SSP_MAX,
+};
+
+/*
+ * SSP_CTRL bit numbers
+ */
+enum {
+ CTRL_READ = 25,
+ CTRL_DATA_XFER = 24,
+ CTRL_ENABLE = 16,
+ CTRL_LONG_REST = 19,
+};
+/*
+ * SSP_STAT bit numbers
+ */
+enum {
+ STAT_BUSY = 0,
+ STAT_DATA_BUSY = 2,
+ STAT_CMD_BUSY = 3,
+ STAT_CARD_DETECT = 28,
+};
+
+typedef struct mxs_ssp_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t r[SSP_MAX];
+ qemu_irq irq_dma, irq_error;
+ SDState *sd;
+} mxs_ssp_state;
+
+static uint64_t mxs_ssp_read(
+ void *opaque, hwaddr offset, unsigned size)
+{
+ mxs_ssp_state *s = (mxs_ssp_state *) opaque;
+ uint32_t res = 0;
+
+ switch (offset >> 4) {
+ case 0 ... SSP_MAX:
+ res = s->r[offset >> 4];
+ switch (offset >> 4) {
+ case SSP_STATUS:
+ s->r[SSP_STATUS] &= ~((1 << STAT_BUSY) |
+ (1 << STAT_DATA_BUSY) | (1 << STAT_CMD_BUSY));
+ break;
+ /* dma polls this register to read the data from the card
+ * this is not very efficient, perhaps a better data conduit
+ * is available. It does work as the real hardware tho...
+ */
+ case SSP_DATA:
+ res = sd_read_data(s->sd);
+ break;
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+
+ return res;
+}
+
+static uint32_t __swap(uint32_t w)
+{
+ return (w >> 24) | ((w & 0x00ff0000) >> 8) |
+ ((w & 0x0000ff00) << 8) | (w << 24);
+}
+
+/*
+ * processes one SD/MMC command train. It always have a 'command' but
+ * can also have datas attached, this case is not handled here, it's
+ * handled by the SD layer.
+ * The command can either be short or long, wierdly, the mxs returns
+ * the bytes in some funky order that needs to be restored.
+ * TODO: Make big endian compatible
+ */
+static void mxs_process_cmd(mxs_ssp_state *s)
+{
+ if (!(s->r[SSP_CTRL] & (1 << CTRL_ENABLE)))
+ return;
+ uint32_t r[4]; // temporary buffer
+
+ s->r[SSP_SDRESP0] = s->r[SSP_SDRESP1] =
+ s->r[SSP_SDRESP2] = s->r[SSP_SDRESP3] = 0;
+
+ SDRequest cmd = {
+ .cmd = s->r[SSP_SD_CMD0] & 0xff,
+ .arg = s->r[SSP_SD_CMD1],
+ .crc = 0,
+ };
+ sd_enable(s->sd, 1);
+ sd_do_command(s->sd, &cmd, (uint8_t*) r);
+ if (s->r[SSP_CTRL] & (1 << CTRL_LONG_REST)) {
+ s->r[SSP_SDRESP0] = __swap(r[3]);
+ s->r[SSP_SDRESP1] = __swap(r[2]);
+ s->r[SSP_SDRESP2] = __swap(r[1]);
+ s->r[SSP_SDRESP3] = __swap(r[0]);
+ } else
+ s->r[SSP_SDRESP0] = __swap(r[0]);
+
+ /* mark these flags as busy, they will be read once
+ * as 'busy' before being cleared by a read. */
+ s->r[SSP_STATUS] |= (1 << STAT_CMD_BUSY);
+ s->r[SSP_STATUS] |= (1 << STAT_BUSY);
+ if (s->r[SSP_CTRL] & (1 << CTRL_DATA_XFER))
+ s->r[SSP_STATUS] |= (1 << STAT_DATA_BUSY);
+}
+
+static void mxs_ssp_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ mxs_ssp_state *s = (mxs_ssp_state *) opaque;
+ uint32_t oldvalue = 0;
+
+ switch (offset >> 4) {
+ case 0 ... SSP_MAX:
+ oldvalue = mxs_write(&s->r[offset >> 4], offset, value, size);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ switch (offset >> 4) {
+ case SSP_CTRL:
+ if ((oldvalue ^ s->r[SSP_CTRL]) == 0x80000000
+ && !(oldvalue & 0x80000000)) {
+ // printf("%s reseting, anding clockgate\n", __func__);
+ // TODO: Implement a reset function?
+ s->r[SSP_CTRL] |= 0x40000000;
+ }
+ break;
+ case SSP_SD_CMD1:
+ mxs_process_cmd(s);
+ break;
+ /*
+ * Write from DMA
+ * TODO: Handle case were it's not a SD/MMC but a normal SPI
+ */
+ case SSP_DATA:
+ sd_write_data(s->sd, s->r[SSP_DATA]);
+ break;
+ case SSP_STATUS:
+ s->r[SSP_STATUS] = oldvalue;
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid write to SSP_STATUS\n", __func__);
+ break;
+ }
+}
+
+
+static const MemoryRegionOps mxs_ssp_ops = {
+ .read = mxs_ssp_read,
+ .write = mxs_ssp_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int mxs_ssp_init(SysBusDevice *dev)
+{
+ mxs_ssp_state *s = OBJECT_CHECK(mxs_ssp_state, dev, "mxs_ssp");
+
+ sysbus_init_irq(dev, &s->irq_dma);
+ sysbus_init_irq(dev, &s->irq_error);
+ memory_region_init_io(&s->iomem, OBJECT(s), &mxs_ssp_ops, s,
+ "mxs_ssp", 0x2000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ s->r[SSP_CTRL] = 0xc0000000;
+ s->r[SSP_STATUS] = 0xe0000000;
+ s->r[SSP_VERSION] = 0x03000000;
+
+ DriveInfo *dinfo = drive_get_next(IF_SD);
+
+ s->sd = sd_init(dinfo ? dinfo->bdrv : NULL, 0);
+
+ return 0;
+}
+
+
+static void mxs_ssp_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = mxs_ssp_init;
+}
+
+static TypeInfo ssp_info = {
+ .name = "mxs_ssp",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mxs_ssp_state),
+ .class_init = mxs_ssp_class_init,
+};
+
+static void mxs_ssp_register(void)
+{
+ type_register_static(&ssp_info);
+}
+
+type_init(mxs_ssp_register)
+
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 08/13] mxs/imx23: Add SSP/SPI driver
2013-12-11 13:56 ` [Qemu-devel] [PATCH 08/13] mxs/imx23: Add SSP/SPI driver Michel Pollet
@ 2014-01-11 9:08 ` Peter Crosthwaite
0 siblings, 0 replies; 36+ messages in thread
From: Peter Crosthwaite @ 2014-01-11 9:08 UTC (permalink / raw)
To: Michel Pollet, Paolo Bonzini; +Cc: qemu-devel@nongnu.org Developers
On Wed, Dec 11, 2013 at 11:56 PM, Michel Pollet <buserror@gmail.com> wrote:
> This implements the SSP port(s) of the mxs. Currently hardcoded for
> the SD card interface, but as TODO it could rather easily be made
> to be generic and support 'generic' SPI too.
> It is geared toward working with DMA, as the linux drivers uses it that
> way.
>
> Signed-off-by: Michel Pollet <buserror@gmail.com>
> ---
> hw/ssi/Makefile.objs | 1 +
> hw/ssi/mxs_spi.c | 239 +++++++++++++++++++++++++++++++++++++++++++++++++++
I'd call it an SD controller for the moment, or put it in misc with a
comment about how you intend to make this work with multiple
interfaces. Paolo may be able to better comment on /hw/ org. A SD
device shouldn't live in ssi.
> 2 files changed, 240 insertions(+)
> create mode 100644 hw/ssi/mxs_spi.c
>
> diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
> index 9555825..72ec849 100644
> --- a/hw/ssi/Makefile.objs
> +++ b/hw/ssi/Makefile.objs
> @@ -4,3 +4,4 @@ common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
> common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
>
> obj-$(CONFIG_OMAP) += omap_spi.o
> +obj-$(CONFIG_MXS) += mxs_spi.o
> diff --git a/hw/ssi/mxs_spi.c b/hw/ssi/mxs_spi.c
> new file mode 100644
> index 0000000..8bc70f9
> --- /dev/null
> +++ b/hw/ssi/mxs_spi.c
> @@ -0,0 +1,239 @@
> +/*
> + * mxs_ssp.c
> + *
> + * Copyright: Michel Pollet <buserror@gmail.com>
> + *
> + * QEMU Licence
> + */
> +
> +/*
> + * This implements the SSP port(s) of the mxs. Currently hardcoded for the
> + * SD card interface, but as TODO it could rather easily be made to be generic
> + * and support 'generic' SPI too.
> + * It is geared toward working with DMA, as the linux drivers uses it that way.
> + */
> +#include "hw/sysbus.h"
> +#include "hw/arm/mxs.h"
> +#include "sysemu/blockdev.h"
> +#include "hw/sd.h"
> +
> +/*
> + * SSP register indexes, most of the useful ones
> + */
> +enum {
> + SSP_CTRL = 0x0,
> + SSP_SD_CMD0 = 0x1,
> + SSP_SD_CMD1 = 0x2,
> + SSP_COMPREF = 0x3,
> + SSP_COMPMASK = 0x4,
> + SSP_TIMING = 0x5,
> + SSP_CTRL1 = 0x6,
> + SSP_DATA = 0x7,
> + SSP_SDRESP0 = 0x8,
> + SSP_SDRESP1 = 0x9,
> + SSP_SDRESP2 = 0xa,
> + SSP_SDRESP3 = 0xb,
> + SSP_STATUS = 0xc,
> +
> + SSP_VERSION = 0x11,
> + SSP_MAX,
> +};
> +
> +/*
> + * SSP_CTRL bit numbers
> + */
> +enum {
> + CTRL_READ = 25,
> + CTRL_DATA_XFER = 24,
> + CTRL_ENABLE = 16,
> + CTRL_LONG_REST = 19,
> +};
> +/*
> + * SSP_STAT bit numbers
> + */
> +enum {
> + STAT_BUSY = 0,
> + STAT_DATA_BUSY = 2,
> + STAT_CMD_BUSY = 3,
> + STAT_CARD_DETECT = 28,
> +};
> +
> +typedef struct mxs_ssp_state {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> +
> + uint32_t r[SSP_MAX];
> + qemu_irq irq_dma, irq_error;
> + SDState *sd;
> +} mxs_ssp_state;
> +
> +static uint64_t mxs_ssp_read(
> + void *opaque, hwaddr offset, unsigned size)
> +{
> + mxs_ssp_state *s = (mxs_ssp_state *) opaque;
> + uint32_t res = 0;
> +
> + switch (offset >> 4) {
> + case 0 ... SSP_MAX:
> + res = s->r[offset >> 4];
> + switch (offset >> 4) {
> + case SSP_STATUS:
> + s->r[SSP_STATUS] &= ~((1 << STAT_BUSY) |
> + (1 << STAT_DATA_BUSY) | (1 << STAT_CMD_BUSY));
> + break;
> + /* dma polls this register to read the data from the card
> + * this is not very efficient, perhaps a better data conduit
> + * is available. It does work as the real hardware tho...
> + */
If thats what real hw does then this is fine. I wouldn't invest in an
app specific side channel or anything like that.
> + case SSP_DATA:
> + res = sd_read_data(s->sd);
> + break;
> + }
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> +
> + return res;
> +}
> +
> +static uint32_t __swap(uint32_t w)
> +{
> + return (w >> 24) | ((w & 0x00ff0000) >> 8) |
> + ((w & 0x0000ff00) << 8) | (w << 24);
> +}
bswap32?
> +
> +/*
> + * processes one SD/MMC command train. It always have a 'command' but
> + * can also have datas attached, this case is not handled here, it's
> + * handled by the SD layer.
> + * The command can either be short or long, wierdly, the mxs returns
> + * the bytes in some funky order that needs to be restored.
> + * TODO: Make big endian compatible
> + */
> +static void mxs_process_cmd(mxs_ssp_state *s)
mxs_process_sd_cmd.
> +{
> + if (!(s->r[SSP_CTRL] & (1 << CTRL_ENABLE)))
> + return;
> + uint32_t r[4]; // temporary buffer
Don't do declarations after code.
> +
> + s->r[SSP_SDRESP0] = s->r[SSP_SDRESP1] =
> + s->r[SSP_SDRESP2] = s->r[SSP_SDRESP3] = 0;
> +
> + SDRequest cmd = {
> + .cmd = s->r[SSP_SD_CMD0] & 0xff,
> + .arg = s->r[SSP_SD_CMD1],
> + .crc = 0,
> + };
> + sd_enable(s->sd, 1);
> + sd_do_command(s->sd, &cmd, (uint8_t*) r);
> + if (s->r[SSP_CTRL] & (1 << CTRL_LONG_REST)) {
> + s->r[SSP_SDRESP0] = __swap(r[3]);
> + s->r[SSP_SDRESP1] = __swap(r[2]);
> + s->r[SSP_SDRESP2] = __swap(r[1]);
> + s->r[SSP_SDRESP3] = __swap(r[0]);
> + } else
> + s->r[SSP_SDRESP0] = __swap(r[0]);
> +
> + /* mark these flags as busy, they will be read once
> + * as 'busy' before being cleared by a read. */
> + s->r[SSP_STATUS] |= (1 << STAT_CMD_BUSY);
> + s->r[SSP_STATUS] |= (1 << STAT_BUSY);
> + if (s->r[SSP_CTRL] & (1 << CTRL_DATA_XFER))
> + s->r[SSP_STATUS] |= (1 << STAT_DATA_BUSY);
> +}
> +
> +static void mxs_ssp_write(void *opaque, hwaddr offset,
> + uint64_t value, unsigned size)
> +{
> + mxs_ssp_state *s = (mxs_ssp_state *) opaque;
> + uint32_t oldvalue = 0;
> +
> + switch (offset >> 4) {
> + case 0 ... SSP_MAX:
> + oldvalue = mxs_write(&s->r[offset >> 4], offset, value, size);
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> + switch (offset >> 4) {
> + case SSP_CTRL:
> + if ((oldvalue ^ s->r[SSP_CTRL]) == 0x80000000
> + && !(oldvalue & 0x80000000)) {
> + // printf("%s reseting, anding clockgate\n", __func__);
> + // TODO: Implement a reset function?
> + s->r[SSP_CTRL] |= 0x40000000;
> + }
> + break;
> + case SSP_SD_CMD1:
> + mxs_process_cmd(s);
> + break;
> + /*
> + * Write from DMA
> + * TODO: Handle case were it's not a SD/MMC but a normal SPI
> + */
A well placed qemu_log_mask(LOG_UNIMP, could make this user friendly.
I dont see functionality with modes here though. Are there any bit
fields that should be set to explicitly put the controller in SD
mode>?
Regards,
Peter
> + case SSP_DATA:
> + sd_write_data(s->sd, s->r[SSP_DATA]);
> + break;
> + case SSP_STATUS:
> + s->r[SSP_STATUS] = oldvalue;
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: invalid write to SSP_STATUS\n", __func__);
> + break;
> + }
> +}
> +
> +
> +static const MemoryRegionOps mxs_ssp_ops = {
> + .read = mxs_ssp_read,
> + .write = mxs_ssp_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static int mxs_ssp_init(SysBusDevice *dev)
> +{
> + mxs_ssp_state *s = OBJECT_CHECK(mxs_ssp_state, dev, "mxs_ssp");
> +
> + sysbus_init_irq(dev, &s->irq_dma);
> + sysbus_init_irq(dev, &s->irq_error);
> + memory_region_init_io(&s->iomem, OBJECT(s), &mxs_ssp_ops, s,
> + "mxs_ssp", 0x2000);
> + sysbus_init_mmio(dev, &s->iomem);
> +
> + s->r[SSP_CTRL] = 0xc0000000;
> + s->r[SSP_STATUS] = 0xe0000000;
> + s->r[SSP_VERSION] = 0x03000000;
> +
> + DriveInfo *dinfo = drive_get_next(IF_SD);
> +
> + s->sd = sd_init(dinfo ? dinfo->bdrv : NULL, 0);
> +
> + return 0;
> +}
> +
> +
> +static void mxs_ssp_class_init(ObjectClass *klass, void *data)
> +{
> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +
> + sdc->init = mxs_ssp_init;
> +}
> +
> +static TypeInfo ssp_info = {
> + .name = "mxs_ssp",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(mxs_ssp_state),
> + .class_init = mxs_ssp_class_init,
> +};
> +
> +static void mxs_ssp_register(void)
> +{
> + type_register_static(&ssp_info);
> +}
> +
> +type_init(mxs_ssp_register)
> +
> --
> 1.8.5.1
>
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 09/13] mxs/imx23: Add the RTC block
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
` (7 preceding siblings ...)
2013-12-11 13:56 ` [Qemu-devel] [PATCH 08/13] mxs/imx23: Add SSP/SPI driver Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2014-01-11 9:16 ` Peter Crosthwaite
2013-12-11 13:56 ` [Qemu-devel] [PATCH 10/13] mxs/imx23: Add the timers Michel Pollet
` (4 subsequent siblings)
13 siblings, 1 reply; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
Support for the RTC IO block on the imx23, enough to return
a valid date and make guest linux happy.
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/timer/Makefile.objs | 1 +
hw/timer/mxs_rtc.c | 147 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 148 insertions(+)
create mode 100644 hw/timer/mxs_rtc.c
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 3ae091c..1003169 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -26,5 +26,6 @@ obj-$(CONFIG_OMAP) += omap_synctimer.o
obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
obj-$(CONFIG_SH4) += sh_timer.o
obj-$(CONFIG_TUSB6010) += tusb6010.o
+obj-$(CONFIG_MXS) += mxs_rtc.o
obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
diff --git a/hw/timer/mxs_rtc.c b/hw/timer/mxs_rtc.c
new file mode 100644
index 0000000..628fade
--- /dev/null
+++ b/hw/timer/mxs_rtc.c
@@ -0,0 +1,147 @@
+/*
+ * mxs_rtc.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ */
+/*
+ * Implement the RTC block of the mxs. At least, the real time stuff.
+ * TODO Alarm, and watchdog ?
+ */
+
+#include "hw/sysbus.h"
+#include "hw/arm/mxs.h"
+
+#define D(w)
+
+enum {
+ RTC_CTRL = 0x0,
+ RTC_STAT = 0x1,
+ RTC_MS = 0x2,
+ RTC_SECONDS = 0x3,
+ RTC_ALARM = 0x4,
+ RTC_WATCHDOG = 0x5,
+ RTC_PERSISTENT0 = 0x6,
+ RTC_PERSISTENT1 = 0x7,
+ RTC_PERSISTENT2 = 0x8,
+ RTC_PERSISTENT3 = 0x9,
+ RTC_PERSISTENT4 = 0xa,
+ RTC_PERSISTENT5 = 0xa,
+ RTC_DEBUG = 0xc,
+ RTC_VERSION = 0xd,
+
+ RTC_MAX,
+};
+typedef struct mxs_rtc_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t base_ms, base_s;
+ uint32_t r[RTC_MAX];
+ qemu_irq alarm_irq;
+ CharDriverState *chr;
+} mxs_rtc_state;
+
+static uint64_t mxs_rtc_read(
+ void *opaque, hwaddr offset, unsigned size)
+{
+ mxs_rtc_state *s = (mxs_rtc_state *) opaque;
+ uint32_t res = 0;
+
+ D(printf("%s %04x (%d) = ", __func__, (int)offset, size);)
+ switch (offset >> 4) {
+ case 0 ... RTC_MAX:
+ if ((offset >> 4) == RTC_MS || (offset >> 4) == RTC_SECONDS) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ s->r[RTC_MS] = (tv.tv_usec / 1000) - s->base_ms;
+ s->r[RTC_SECONDS] = tv.tv_sec - s->base_s;
+ }
+ res = s->r[offset >> 4];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ D(printf("%08x\n", res);)
+
+ return res;
+}
+
+static void mxs_rtc_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ mxs_rtc_state *s = (mxs_rtc_state *) opaque;
+ uint32_t oldvalue = 0;
+
+ D(printf("%s %04x %08x(%d)\n", __func__, (int)offset, (int)value, size);)
+ switch (offset >> 4) {
+ case 0 ... RTC_MAX:
+ mxs_write(&s->r[offset >> 4], offset, value, size);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ switch (offset >> 4) {
+ case RTC_MS:
+ s->base_ms = s->r[RTC_MS];
+ break;
+ case RTC_SECONDS:
+ s->base_s = s->r[RTC_SECONDS];
+ break;
+ case RTC_CTRL:
+ if ((oldvalue ^ s->r[RTC_CTRL]) == 0x80000000
+ && !(oldvalue & 0x80000000)) {
+ D(printf("%s reseting, anding clockgate\n", __func__);)
+ s->r[RTC_CTRL] |= 0x40000000;
+ }
+ break;
+ }
+}
+
+static const MemoryRegionOps mxs_rtc_ops = {
+ .read = mxs_rtc_read,
+ .write = mxs_rtc_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int mxs_rtc_init(SysBusDevice *dev)
+{
+ mxs_rtc_state *s = OBJECT_CHECK(mxs_rtc_state, dev, "mxs_rtc");
+
+ sysbus_init_irq(dev, &s->alarm_irq);
+ memory_region_init_io(&s->iomem, OBJECT(s), &mxs_rtc_ops, s,
+ "mxs_rtc", 0x2000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ s->r[RTC_CTRL] = 0xc0000000;
+ s->r[RTC_STAT] = 0xe80f0000;
+ s->r[RTC_VERSION] = 0x02000000;
+ // s->base_s = time(NULL);
+ return 0;
+}
+
+
+static void mxs_rtc_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = mxs_rtc_init;
+}
+
+static TypeInfo rtc_info = {
+ .name = "mxs_rtc",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mxs_rtc_state),
+ .class_init = mxs_rtc_class_init,
+};
+
+static void mxs_rtc_register(void)
+{
+ type_register_static(&rtc_info);
+}
+
+type_init(mxs_rtc_register)
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 09/13] mxs/imx23: Add the RTC block
2013-12-11 13:56 ` [Qemu-devel] [PATCH 09/13] mxs/imx23: Add the RTC block Michel Pollet
@ 2014-01-11 9:16 ` Peter Crosthwaite
0 siblings, 0 replies; 36+ messages in thread
From: Peter Crosthwaite @ 2014-01-11 9:16 UTC (permalink / raw)
To: Michel Pollet; +Cc: qemu-devel@nongnu.org Developers
On Wed, Dec 11, 2013 at 11:56 PM, Michel Pollet <buserror@gmail.com> wrote:
> Support for the RTC IO block on the imx23, enough to return
> a valid date and make guest linux happy.
>
> Signed-off-by: Michel Pollet <buserror@gmail.com>
> ---
> hw/timer/Makefile.objs | 1 +
> hw/timer/mxs_rtc.c | 147 +++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 148 insertions(+)
> create mode 100644 hw/timer/mxs_rtc.c
>
> diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
> index 3ae091c..1003169 100644
> --- a/hw/timer/Makefile.objs
> +++ b/hw/timer/Makefile.objs
> @@ -26,5 +26,6 @@ obj-$(CONFIG_OMAP) += omap_synctimer.o
> obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
> obj-$(CONFIG_SH4) += sh_timer.o
> obj-$(CONFIG_TUSB6010) += tusb6010.o
> +obj-$(CONFIG_MXS) += mxs_rtc.o
>
> obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
> diff --git a/hw/timer/mxs_rtc.c b/hw/timer/mxs_rtc.c
> new file mode 100644
> index 0000000..628fade
> --- /dev/null
> +++ b/hw/timer/mxs_rtc.c
> @@ -0,0 +1,147 @@
> +/*
> + * mxs_rtc.c
> + *
> + * Copyright: Michel Pollet <buserror@gmail.com>
> + *
> + * QEMU Licence
> + */
> +/*
> + * Implement the RTC block of the mxs. At least, the real time stuff.
> + * TODO Alarm, and watchdog ?
> + */
> +
> +#include "hw/sysbus.h"
> +#include "hw/arm/mxs.h"
> +
> +#define D(w)
> +
> +enum {
> + RTC_CTRL = 0x0,
> + RTC_STAT = 0x1,
> + RTC_MS = 0x2,
> + RTC_SECONDS = 0x3,
> + RTC_ALARM = 0x4,
> + RTC_WATCHDOG = 0x5,
> + RTC_PERSISTENT0 = 0x6,
> + RTC_PERSISTENT1 = 0x7,
> + RTC_PERSISTENT2 = 0x8,
> + RTC_PERSISTENT3 = 0x9,
> + RTC_PERSISTENT4 = 0xa,
> + RTC_PERSISTENT5 = 0xa,
> + RTC_DEBUG = 0xc,
> + RTC_VERSION = 0xd,
> +
> + RTC_MAX,
> +};
> +typedef struct mxs_rtc_state {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> + uint32_t base_ms, base_s;
> + uint32_t r[RTC_MAX];
> + qemu_irq alarm_irq;
> + CharDriverState *chr;
> +} mxs_rtc_state;
> +
> +static uint64_t mxs_rtc_read(
> + void *opaque, hwaddr offset, unsigned size)
> +{
> + mxs_rtc_state *s = (mxs_rtc_state *) opaque;
> + uint32_t res = 0;
> +
> + D(printf("%s %04x (%d) = ", __func__, (int)offset, size);)
> + switch (offset >> 4) {
> + case 0 ... RTC_MAX:
> + if ((offset >> 4) == RTC_MS || (offset >> 4) == RTC_SECONDS) {
> + struct timeval tv;
> + gettimeofday(&tv, NULL);
> + s->r[RTC_MS] = (tv.tv_usec / 1000) - s->base_ms;
> + s->r[RTC_SECONDS] = tv.tv_sec - s->base_s;
> + }
> + res = s->r[offset >> 4];
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
So for these peripherals that are just a straight up register set of
R_MAX, can you define the MMIO region as R_MAX*4 rather than 0x2000?
Then you can drop these log_guest_errors that are trying to trap
accesses outside the peripheral. Whats the architectural significance
of 0x2000?
Regards,
Peter
> + }
> + D(printf("%08x\n", res);)
> +
> + return res;
> +}
> +
> +static void mxs_rtc_write(void *opaque, hwaddr offset,
> + uint64_t value, unsigned size)
> +{
> + mxs_rtc_state *s = (mxs_rtc_state *) opaque;
> + uint32_t oldvalue = 0;
> +
> + D(printf("%s %04x %08x(%d)\n", __func__, (int)offset, (int)value, size);)
> + switch (offset >> 4) {
> + case 0 ... RTC_MAX:
> + mxs_write(&s->r[offset >> 4], offset, value, size);
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> + switch (offset >> 4) {
> + case RTC_MS:
> + s->base_ms = s->r[RTC_MS];
> + break;
> + case RTC_SECONDS:
> + s->base_s = s->r[RTC_SECONDS];
> + break;
> + case RTC_CTRL:
> + if ((oldvalue ^ s->r[RTC_CTRL]) == 0x80000000
> + && !(oldvalue & 0x80000000)) {
> + D(printf("%s reseting, anding clockgate\n", __func__);)
> + s->r[RTC_CTRL] |= 0x40000000;
> + }
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps mxs_rtc_ops = {
> + .read = mxs_rtc_read,
> + .write = mxs_rtc_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static int mxs_rtc_init(SysBusDevice *dev)
> +{
> + mxs_rtc_state *s = OBJECT_CHECK(mxs_rtc_state, dev, "mxs_rtc");
> +
> + sysbus_init_irq(dev, &s->alarm_irq);
> + memory_region_init_io(&s->iomem, OBJECT(s), &mxs_rtc_ops, s,
> + "mxs_rtc", 0x2000);
> + sysbus_init_mmio(dev, &s->iomem);
> +
> + s->r[RTC_CTRL] = 0xc0000000;
> + s->r[RTC_STAT] = 0xe80f0000;
> + s->r[RTC_VERSION] = 0x02000000;
> + // s->base_s = time(NULL);
> + return 0;
> +}
> +
> +
> +static void mxs_rtc_class_init(ObjectClass *klass, void *data)
> +{
> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +
> + sdc->init = mxs_rtc_init;
> +}
> +
> +static TypeInfo rtc_info = {
> + .name = "mxs_rtc",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(mxs_rtc_state),
> + .class_init = mxs_rtc_class_init,
> +};
> +
> +static void mxs_rtc_register(void)
> +{
> + type_register_static(&rtc_info);
> +}
> +
> +type_init(mxs_rtc_register)
> --
> 1.8.5.1
>
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 10/13] mxs/imx23: Add the timers
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
` (8 preceding siblings ...)
2013-12-11 13:56 ` [Qemu-devel] [PATCH 09/13] mxs/imx23: Add the RTC block Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2013-12-11 13:56 ` [Qemu-devel] [PATCH 11/13] mxs/imx23: Add the USB driver Michel Pollet
` (3 subsequent siblings)
13 siblings, 0 replies; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
Support for the timer IO block of the mxs/imx23. Does not support
any of the fancy function, just the 32khz based timers used by
linux.
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/timer/Makefile.objs | 2 +-
hw/timer/mxs_timrot.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 272 insertions(+), 1 deletion(-)
create mode 100644 hw/timer/mxs_timrot.c
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index 1003169..8d2933a 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -26,6 +26,6 @@ obj-$(CONFIG_OMAP) += omap_synctimer.o
obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
obj-$(CONFIG_SH4) += sh_timer.o
obj-$(CONFIG_TUSB6010) += tusb6010.o
-obj-$(CONFIG_MXS) += mxs_rtc.o
+obj-$(CONFIG_MXS) += mxs_rtc.o mxs_timrot.o
obj-$(CONFIG_MC146818RTC) += mc146818rtc.o
diff --git a/hw/timer/mxs_timrot.c b/hw/timer/mxs_timrot.c
new file mode 100644
index 0000000..b54c0b1
--- /dev/null
+++ b/hw/timer/mxs_timrot.c
@@ -0,0 +1,271 @@
+/*
+ * mxs_timrot.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ */
+
+/*
+ * Implements the timer block for the mxs. Currently supports only the
+ * 32khz based clock, and not all the of the options, nor the input counters,
+ * PWM etc etc.
+ * Basically, it supports enough for the linux kernel
+ */
+#include "hw/sysbus.h"
+#include "hw/arm/mxs.h"
+#include "hw/ptimer.h"
+#include "qemu/main-loop.h"
+
+enum {
+ TIMROT_ROTCTRL = 0,
+ TIMROT_CTRL0 = 0x2,
+ TIMROT_COUNT0 = 0x3,
+ TIMROT_CTRL1 = 0x4,
+ TIMROT_COUNT1 = 0x5,
+ TIMROT_CTRL2 = 0x6,
+ TIMROT_COUNT2 = 0x7,
+ TIMROT_CTRL3 = 0x8,
+ TIMROT_COUNT3 = 0x9,
+ TIMROT_VERSION = 0xa,
+};
+
+enum {
+ TIM_IRQ = 15,
+ TIM_IRQ_EN = 14,
+ TIM_UPDATE = 7,
+ TIM_RELOAD = 6,
+ TIM_PRESCALE = 4,
+ TIM_SELECT = 0,
+};
+
+typedef struct mxs_tim_state {
+ struct mxs_timrot_state * s;
+ uint8_t tid;
+ uint8_t fired;
+ uint32_t control, count;
+ qemu_irq irq;
+ ptimer_state * timer;
+} mxs_tim_state;
+
+typedef struct mxs_timrot_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t rotctrl;
+
+ mxs_tim_state t[4];
+} mxs_timrot_state;
+
+static void tim_set_count(mxs_tim_state * t, uint32_t count)
+{
+ if (count != (t->count & 0xffff) || t->fired) {
+ t->count = (t->count & ~0xffff) | (count & 0xffff);
+ ptimer_set_limit(t->timer, t->count & 0xffff, 1);
+ if (t->count & 0xffff) {
+ t->fired = 0;
+ ptimer_run(t->timer, t->control & (1 << TIM_RELOAD) ? 0 : 1);
+ }
+ }
+}
+
+static void tim_set_control(mxs_tim_state * t, uint16_t control)
+{
+ uint32_t change = t->control ^ control;
+ if (!change) {
+ return;
+ }
+
+ uint32_t freq = 0;
+ switch ((control >> TIM_SELECT) & 0xf) {
+ case 0x8:
+ freq = 32000;
+ break;
+ case 0x9:
+ freq = 8000;
+ break;
+ case 0xa:
+ freq = 4000;
+ break;
+ case 0xc:
+ freq = 1000;
+ break;
+ }
+ switch ((control >> TIM_PRESCALE) & 0x3) {
+ /* TODO */
+ }
+ if (!(control & (1 << TIM_IRQ))) {
+ qemu_irq_lower(t->irq);
+ }
+ if (freq == 0) {
+ ptimer_stop(t->timer);
+ } else if (change & 0xff) {
+ printf("%s[%d] %04x freq %d\n", __func__, t->tid, control, (int) freq);
+ ptimer_set_freq(t->timer, freq);
+ ptimer_set_limit(t->timer, t->count & 0xffff, 1);
+ if (t->count & 0xffff) {
+ t->fired = 0;
+ ptimer_run(t->timer, control & (1 << TIM_RELOAD) ? 0 : 1);
+ }
+ }
+ t->control = control;
+}
+
+static uint32_t tim_get_count(mxs_tim_state * t)
+{
+ t->count &= 0xffff;
+ t->count |= (ptimer_get_count(t->timer) << 16);
+ return t->count;
+}
+
+static void mxs_timrot_timer_trigger(void *opaque)
+{
+ mxs_tim_state * t = opaque;
+ t->fired = 1;
+ t->control |= (1 << TIM_IRQ);
+ if (t->control & (1 << TIM_IRQ_EN))
+ qemu_irq_raise(t->irq);
+}
+
+static inline int tim_get_tid(hwaddr offset)
+{
+ return ((offset >> 4) - TIMROT_CTRL0) >> 1;
+}
+
+static uint64_t mxs_timrot_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ mxs_timrot_state *s = (mxs_timrot_state *) opaque;
+ uint32_t res = 0;
+
+ switch (offset >> 4) {
+ case TIMROT_ROTCTRL:
+ res = s->rotctrl | (0xf << 25);
+ break;
+ case TIMROT_VERSION:
+ res = 0x01010000;
+ break;
+ case TIMROT_CTRL0:
+ case TIMROT_CTRL1:
+ case TIMROT_CTRL2:
+ case TIMROT_CTRL3:
+ res = s->t[tim_get_tid(offset)].control;
+ break;
+ case TIMROT_COUNT0:
+ case TIMROT_COUNT1:
+ case TIMROT_COUNT2:
+ case TIMROT_COUNT3:
+ res = tim_get_count(&s->t[tim_get_tid(offset)]);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ return 0;
+ }
+ return res;
+}
+
+static void mxs_timrot_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ mxs_timrot_state *s = (mxs_timrot_state *) opaque;
+ uint32_t * dst = NULL;
+ uint32_t val = 0;
+ uint32_t oldvalue = 0;
+
+ switch (offset >> 4) {
+ case TIMROT_ROTCTRL:
+ dst = &s->rotctrl;
+ break;
+ case TIMROT_CTRL0:
+ case TIMROT_CTRL1:
+ case TIMROT_CTRL2:
+ case TIMROT_CTRL3:
+ val = s->t[tim_get_tid(offset)].control;
+ dst = &val;
+ break;
+ case TIMROT_COUNT0:
+ case TIMROT_COUNT1:
+ case TIMROT_COUNT2:
+ case TIMROT_COUNT3:
+ val = s->t[tim_get_tid(offset)].count;
+ dst = &val;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ return;
+ }
+ if (!dst) {
+ return;
+ }
+ oldvalue = mxs_write(dst, offset, value, size);
+
+ switch (offset >> 4) {
+ case TIMROT_ROTCTRL:
+ if ((oldvalue ^ *dst) == 0x80000000 && !(oldvalue & 0x80000000)) {
+ // printf("%s reseting, anding clockgate\n", __func__);
+ *dst |= 0x40000000;
+ }
+ *dst |= 0xf << 25; // 4 timers, no encoder
+ break;
+ case TIMROT_CTRL0:
+ case TIMROT_CTRL1:
+ case TIMROT_CTRL2:
+ case TIMROT_CTRL3:
+ tim_set_control(&s->t[tim_get_tid(offset)], val);
+ break;
+ case TIMROT_COUNT0:
+ case TIMROT_COUNT1:
+ case TIMROT_COUNT2:
+ case TIMROT_COUNT3:
+ tim_set_count(&s->t[tim_get_tid(offset)], val);
+ break;
+ }
+}
+
+
+static const MemoryRegionOps mxs_timrot_ops = {
+ .read = mxs_timrot_read,
+ .write = mxs_timrot_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int mxs_timrot_init(SysBusDevice *dev)
+{
+ mxs_timrot_state *s = OBJECT_CHECK(mxs_timrot_state, dev, "mxs_timrot");
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ QEMUBH *bh = qemu_bh_new(mxs_timrot_timer_trigger, &s->t[i]);
+ s->t[i].timer = ptimer_init(bh);
+ sysbus_init_irq(dev, &s->t[i].irq);
+ s->t[i].s = s;
+ s->t[i].tid = i;
+ }
+ memory_region_init_io(&s->iomem, OBJECT(s), &mxs_timrot_ops, s,
+ "mxs_timrot", 0x2000);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+static void mxs_timrot_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = mxs_timrot_init;
+}
+
+static TypeInfo timrot_info = {
+ .name = "mxs_timrot",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mxs_timrot_state),
+ .class_init = mxs_timrot_class_init,
+};
+
+static void mxs_timrot_register(void)
+{
+ type_register_static(&timrot_info);
+}
+
+type_init(mxs_timrot_register)
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 11/13] mxs/imx23: Add the USB driver
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
` (9 preceding siblings ...)
2013-12-11 13:56 ` [Qemu-devel] [PATCH 10/13] mxs/imx23: Add the timers Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2014-01-11 9:57 ` Peter Crosthwaite
2013-12-11 13:56 ` [Qemu-devel] [PATCH 12/13] mxs/imx23: Main core instantiation and minor IO blocks Michel Pollet
` (2 subsequent siblings)
13 siblings, 1 reply; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
Add the USB IO block, and the USB PHY IO block. This just wraps
an ehci instance, and support some of the 'extra' mxs registers
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/usb/Makefile.objs | 1 +
hw/usb/mxs_usb.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 255 insertions(+)
create mode 100644 hw/usb/mxs_usb.c
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index a3eac3e..58d9cf1 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -8,6 +8,7 @@ common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o
common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o hcd-ehci-sysbus.o
common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o
common-obj-$(CONFIG_USB_MUSB) += hcd-musb.o
+common-obj-$(CONFIG_MXS) += mxs_usb.o
# emulated usb devices
common-obj-y += dev-hub.o
diff --git a/hw/usb/mxs_usb.c b/hw/usb/mxs_usb.c
new file mode 100644
index 0000000..1be5f37
--- /dev/null
+++ b/hw/usb/mxs_usb.c
@@ -0,0 +1,254 @@
+/*
+ * mxs_usb.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ */
+
+/*
+ * Implements the USB block of the mxs. This is just a case of
+ * instantiating a ehci block, and have a few read only registers
+ * for mxs specific bits
+ */
+#include "hw/sysbus.h"
+#include "hw/arm/mxs.h"
+#include "hw/usb/hcd-ehci.h"
+#include "hw/qdev.h"
+
+#define D(w)
+
+enum {
+ USB_MAX = 256 / 4,
+};
+
+typedef struct mxs_usb_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t r[USB_MAX];
+ qemu_irq irq_dma, irq_error;
+
+ EHCIState ehci;
+} mxs_usb_state;
+
+static uint64_t mxs_usb_read(
+ void *opaque, hwaddr offset, unsigned size)
+{
+ mxs_usb_state *s = (mxs_usb_state *) opaque;
+ uint32_t res = 0;
+
+ D(printf("%s %04x (%d) = ", __func__, (int)offset, size);)
+ switch (offset >> 2) {
+ case 0 ... USB_MAX:
+ res = s->r[offset >> 2];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ D(printf("%08x\n", res);)
+
+ return res;
+}
+
+static void mxs_usb_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ mxs_usb_state *s = (mxs_usb_state *) opaque;
+
+ D(printf("%s %04x %08x(%d)\n", __func__, (int)offset, (int)value, size);)
+ switch (offset) {
+ case 0 ... USB_MAX:
+ s->r[offset] = value;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps mxs_usb_ops = {
+ .read = mxs_usb_read,
+ .write = mxs_usb_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int mxs_usb_init(SysBusDevice *dev)
+{
+ mxs_usb_state *s = OBJECT_CHECK(mxs_usb_state, dev, "mxs_usb");
+ EHCIState *u = &s->ehci;
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &mxs_usb_ops, s,
+ "mxs_usb", 0x100);
+
+ s->r[0] = 0xe241fa05;
+ s->r[0x04 >> 2] = 0x00000015;
+ s->r[0x08 >> 2] = 0x10020001;
+ s->r[0x0c >> 2] = 0x0000000b;
+ s->r[0x10 >> 2] = 0x40060910;
+ s->r[0x14 >> 2] = 0x00000710;
+
+ u->capsbase = 0x100;
+ u->opregbase = 0x140;
+ // FIXME ?!?!?
+// u->dma = &dma_context_memory;
+
+ usb_ehci_init(u, DEVICE(dev));
+ sysbus_init_irq(dev, &u->irq);
+
+ memory_region_add_subregion(&u->mem, 0x0, &s->iomem);
+ sysbus_init_mmio(dev, &u->mem);
+
+ D(printf("%s created bus %s\n", __func__, u->bus.qbus.name);)
+#if 0
+ /*
+ * This is suposed to make companion ports that will support
+ * slower speed devices (mouse/keyboard etc). It's inspired
+ * from ehci/pci however it doesn't work, right now...
+ */
+ int i;
+ for (i = 0; i < NB_PORTS; i += 2) {
+ DeviceState * d = qdev_create(NULL, "sysbus-ohci");
+ qdev_prop_set_string(d, "masterbus", u->bus.qbus.name);
+ qdev_prop_set_uint32(d, "firstport", i);
+ qdev_prop_set_uint32(d, "num-ports", 2);
+ qdev_init_nofail(d);
+ sysbus_connect_irq(SYS_BUS_DEVICE(d), 0, u->irq);
+ }
+#endif
+ return 0;
+}
+
+static void mxs_usb_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = mxs_usb_init;
+}
+
+static TypeInfo mxs_usb_info = {
+ .name = "mxs_usb",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mxs_usb_state),
+ .class_init = mxs_usb_class_init,
+};
+
+static void mxs_usb_register(void)
+{
+ type_register_static(&mxs_usb_info);
+}
+
+type_init(mxs_usb_register)
+
+#undef D
+#define D(w)
+
+enum {
+ USBPHY_PWD = 0x0,
+ USBPHY_TX = 0x1,
+ USBPHY_RX = 0x2,
+ USBPHY_CTRL = 0x3,
+ USBPHY_MAX = 10,
+};
+typedef struct mxs_usbphy_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+
+ uint32_t r[USBPHY_MAX];
+} mxs_usbphy_state;
+
+static uint64_t mxs_usbphy_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ mxs_usbphy_state *s = (mxs_usbphy_state *) opaque;
+ uint32_t res = 0;
+
+ D(printf("%s %04x (%d) = ", __func__, (int)offset, size);)
+ switch (offset >> 4) {
+ case 0 ... USBPHY_MAX:
+ res = s->r[offset >> 4];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ D(printf("%08x\n", res);)
+
+ return res;
+}
+
+static void mxs_usbphy_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ mxs_usbphy_state *s = (mxs_usbphy_state *) opaque;
+ uint32_t oldvalue = 0;
+
+ D(printf("%s %04x %08x(%d) = ", __func__, (int)offset, (int)value, size);)
+ switch (offset >> 4) {
+ case 0 ... USBPHY_MAX:
+ oldvalue = mxs_write(&s->r[offset >> 4], offset, value, size);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ return;
+ }
+ switch (offset >> 4) {
+ case USBPHY_CTRL:
+ if ((oldvalue ^ s->r[USBPHY_CTRL]) == 0x80000000
+ && !(oldvalue & 0x80000000)) {
+ D(printf("%s reseting, anding clockgate\n", __func__);)
+ s->r[USBPHY_CTRL] |= 0x40000000;
+ }
+ break;
+ }
+ D(printf("%08x\n", s->r[offset >> 4]);)
+}
+
+
+static const MemoryRegionOps mxs_usbphy_ops = {
+ .read = mxs_usbphy_read,
+ .write = mxs_usbphy_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int mxs_usbphy_init(SysBusDevice *dev)
+{
+ mxs_usbphy_state *s = OBJECT_CHECK(mxs_usbphy_state, dev, "mxs_usbphy");
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &mxs_usbphy_ops, s,
+ "mxs_usbphy", 0x2000);
+ sysbus_init_mmio(dev, &s->iomem);
+
+ s->r[USBPHY_PWD] = 0x00860607;
+ s->r[USBPHY_PWD] = 0x00860607;
+ s->r[USBPHY_CTRL] = 0xc0000000;
+ return 0;
+}
+
+
+static void mxs_usbphy_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = mxs_usbphy_init;
+}
+
+static TypeInfo usbphy_info = {
+ .name = "mxs_usbphy",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mxs_usbphy_state),
+ .class_init = mxs_usbphy_class_init,
+};
+
+static void mxs_usbphy_register(void)
+{
+ type_register_static(&usbphy_info);
+}
+
+type_init(mxs_usbphy_register)
+
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 11/13] mxs/imx23: Add the USB driver
2013-12-11 13:56 ` [Qemu-devel] [PATCH 11/13] mxs/imx23: Add the USB driver Michel Pollet
@ 2014-01-11 9:57 ` Peter Crosthwaite
0 siblings, 0 replies; 36+ messages in thread
From: Peter Crosthwaite @ 2014-01-11 9:57 UTC (permalink / raw)
To: Michel Pollet, Gerd Hoffmann, Andreas Färber
Cc: qemu-devel@nongnu.org Developers
On Wed, Dec 11, 2013 at 11:56 PM, Michel Pollet <buserror@gmail.com> wrote:
> Add the USB IO block, and the USB PHY IO block. This just wraps
> an ehci instance, and support some of the 'extra' mxs registers
>
> Signed-off-by: Michel Pollet <buserror@gmail.com>
> ---
> hw/usb/Makefile.objs | 1 +
> hw/usb/mxs_usb.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 255 insertions(+)
> create mode 100644 hw/usb/mxs_usb.c
>
> diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
> index a3eac3e..58d9cf1 100644
> --- a/hw/usb/Makefile.objs
> +++ b/hw/usb/Makefile.objs
> @@ -8,6 +8,7 @@ common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o
> common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o hcd-ehci-sysbus.o
> common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o
> common-obj-$(CONFIG_USB_MUSB) += hcd-musb.o
> +common-obj-$(CONFIG_MXS) += mxs_usb.o
>
> # emulated usb devices
> common-obj-y += dev-hub.o
> diff --git a/hw/usb/mxs_usb.c b/hw/usb/mxs_usb.c
> new file mode 100644
> index 0000000..1be5f37
> --- /dev/null
> +++ b/hw/usb/mxs_usb.c
> @@ -0,0 +1,254 @@
> +/*
> + * mxs_usb.c
> + *
> + * Copyright: Michel Pollet <buserror@gmail.com>
> + *
> + * QEMU Licence
> + */
> +
> +/*
> + * Implements the USB block of the mxs. This is just a case of
> + * instantiating a ehci block, and have a few read only registers
> + * for mxs specific bits
> + */
Sounds like a subclass of EHCI. There are several EHCI subclasses
already for this very purpose. Check the TEGRA2 or EXYNOS_4210 EHCI
variations for examples on how to sublcass EHCI.
Regards,
Peter
> +#include "hw/sysbus.h"
> +#include "hw/arm/mxs.h"
> +#include "hw/usb/hcd-ehci.h"
> +#include "hw/qdev.h"
> +
> +#define D(w)
> +
> +enum {
> + USB_MAX = 256 / 4,
> +};
> +
> +typedef struct mxs_usb_state {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> +
> + uint32_t r[USB_MAX];
> + qemu_irq irq_dma, irq_error;
> +
> + EHCIState ehci;
> +} mxs_usb_state;
> +
> +static uint64_t mxs_usb_read(
> + void *opaque, hwaddr offset, unsigned size)
> +{
> + mxs_usb_state *s = (mxs_usb_state *) opaque;
> + uint32_t res = 0;
> +
> + D(printf("%s %04x (%d) = ", __func__, (int)offset, size);)
> + switch (offset >> 2) {
> + case 0 ... USB_MAX:
> + res = s->r[offset >> 2];
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> + D(printf("%08x\n", res);)
> +
> + return res;
> +}
> +
> +static void mxs_usb_write(void *opaque, hwaddr offset,
> + uint64_t value, unsigned size)
> +{
> + mxs_usb_state *s = (mxs_usb_state *) opaque;
> +
> + D(printf("%s %04x %08x(%d)\n", __func__, (int)offset, (int)value, size);)
> + switch (offset) {
> + case 0 ... USB_MAX:
> + s->r[offset] = value;
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps mxs_usb_ops = {
> + .read = mxs_usb_read,
> + .write = mxs_usb_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static int mxs_usb_init(SysBusDevice *dev)
> +{
> + mxs_usb_state *s = OBJECT_CHECK(mxs_usb_state, dev, "mxs_usb");
> + EHCIState *u = &s->ehci;
> +
> + memory_region_init_io(&s->iomem, OBJECT(s), &mxs_usb_ops, s,
> + "mxs_usb", 0x100);
> +
> + s->r[0] = 0xe241fa05;
> + s->r[0x04 >> 2] = 0x00000015;
> + s->r[0x08 >> 2] = 0x10020001;
> + s->r[0x0c >> 2] = 0x0000000b;
> + s->r[0x10 >> 2] = 0x40060910;
> + s->r[0x14 >> 2] = 0x00000710;
> +
> + u->capsbase = 0x100;
> + u->opregbase = 0x140;
This is nice and data driven in the subclassing framwork.
> + // FIXME ?!?!?
> +// u->dma = &dma_context_memory;
> +
> + usb_ehci_init(u, DEVICE(dev));
> + sysbus_init_irq(dev, &u->irq);
> +
> + memory_region_add_subregion(&u->mem, 0x0, &s->iomem);
> + sysbus_init_mmio(dev, &u->mem);
> +
And all you need to do it tack this on in your IMX_EHCI specific instance_init.
> + D(printf("%s created bus %s\n", __func__, u->bus.qbus.name);)
> +#if 0
> + /*
> + * This is suposed to make companion ports that will support
> + * slower speed devices (mouse/keyboard etc). It's inspired
> + * from ehci/pci however it doesn't work, right now...
> + */
> + int i;
> + for (i = 0; i < NB_PORTS; i += 2) {
> + DeviceState * d = qdev_create(NULL, "sysbus-ohci");
> + qdev_prop_set_string(d, "masterbus", u->bus.qbus.name);
> + qdev_prop_set_uint32(d, "firstport", i);
> + qdev_prop_set_uint32(d, "num-ports", 2);
> + qdev_init_nofail(d);
> + sysbus_connect_irq(SYS_BUS_DEVICE(d), 0, u->irq);
> + }
> +#endif
> + return 0;
> +}
> +
> +static void mxs_usb_class_init(ObjectClass *klass, void *data)
> +{
> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +
> + sdc->init = mxs_usb_init;
> +}
> +
> +static TypeInfo mxs_usb_info = {
> + .name = "mxs_usb",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(mxs_usb_state),
> + .class_init = mxs_usb_class_init,
> +};
> +
> +static void mxs_usb_register(void)
> +{
> + type_register_static(&mxs_usb_info);
> +}
> +
> +type_init(mxs_usb_register)
> +
> +#undef D
> +#define D(w)
> +
AFAICT, there is no connectivity between you EHCI and PHY (probably a
good thing at this stage), leading me to believe your PHY is just a
"keep the guest happy" dummy.
With no interconnectivity, It should be a separate device in a
separate file and patch.
Regards,
Peter
> +enum {
> + USBPHY_PWD = 0x0,
> + USBPHY_TX = 0x1,
> + USBPHY_RX = 0x2,
> + USBPHY_CTRL = 0x3,
> + USBPHY_MAX = 10,
> +};
> +typedef struct mxs_usbphy_state {
> + SysBusDevice busdev;
> + MemoryRegion iomem;
> +
> + uint32_t r[USBPHY_MAX];
> +} mxs_usbphy_state;
> +
> +static uint64_t mxs_usbphy_read(void *opaque, hwaddr offset,
> + unsigned size)
> +{
> + mxs_usbphy_state *s = (mxs_usbphy_state *) opaque;
> + uint32_t res = 0;
> +
> + D(printf("%s %04x (%d) = ", __func__, (int)offset, size);)
> + switch (offset >> 4) {
> + case 0 ... USBPHY_MAX:
> + res = s->r[offset >> 4];
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + break;
> + }
> + D(printf("%08x\n", res);)
> +
> + return res;
> +}
> +
> +static void mxs_usbphy_write(void *opaque, hwaddr offset,
> + uint64_t value, unsigned size)
> +{
> + mxs_usbphy_state *s = (mxs_usbphy_state *) opaque;
> + uint32_t oldvalue = 0;
> +
> + D(printf("%s %04x %08x(%d) = ", __func__, (int)offset, (int)value, size);)
> + switch (offset >> 4) {
> + case 0 ... USBPHY_MAX:
> + oldvalue = mxs_write(&s->r[offset >> 4], offset, value, size);
> + break;
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: bad offset 0x%x\n", __func__, (int) offset);
> + return;
> + }
> + switch (offset >> 4) {
> + case USBPHY_CTRL:
> + if ((oldvalue ^ s->r[USBPHY_CTRL]) == 0x80000000
> + && !(oldvalue & 0x80000000)) {
> + D(printf("%s reseting, anding clockgate\n", __func__);)
> + s->r[USBPHY_CTRL] |= 0x40000000;
> + }
> + break;
> + }
> + D(printf("%08x\n", s->r[offset >> 4]);)
> +}
> +
> +
> +static const MemoryRegionOps mxs_usbphy_ops = {
> + .read = mxs_usbphy_read,
> + .write = mxs_usbphy_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
> +};
> +
> +static int mxs_usbphy_init(SysBusDevice *dev)
> +{
> + mxs_usbphy_state *s = OBJECT_CHECK(mxs_usbphy_state, dev, "mxs_usbphy");
> +
> + memory_region_init_io(&s->iomem, OBJECT(s), &mxs_usbphy_ops, s,
> + "mxs_usbphy", 0x2000);
> + sysbus_init_mmio(dev, &s->iomem);
> +
> + s->r[USBPHY_PWD] = 0x00860607;
> + s->r[USBPHY_PWD] = 0x00860607;
> + s->r[USBPHY_CTRL] = 0xc0000000;
> + return 0;
> +}
> +
> +
> +static void mxs_usbphy_class_init(ObjectClass *klass, void *data)
> +{
> + SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
> +
> + sdc->init = mxs_usbphy_init;
> +}
> +
> +static TypeInfo usbphy_info = {
> + .name = "mxs_usbphy",
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(mxs_usbphy_state),
> + .class_init = mxs_usbphy_class_init,
> +};
> +
> +static void mxs_usbphy_register(void)
> +{
> + type_register_static(&usbphy_info);
> +}
> +
> +type_init(mxs_usbphy_register)
> +
> --
> 1.8.5.1
>
>
^ permalink raw reply [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 12/13] mxs/imx23: Main core instantiation and minor IO blocks
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
` (10 preceding siblings ...)
2013-12-11 13:56 ` [Qemu-devel] [PATCH 11/13] mxs/imx23: Add the USB driver Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2013-12-11 13:56 ` [Qemu-devel] [PATCH 13/13] mxs/imx23: Adds support for an Olinuxino board Michel Pollet
2013-12-13 12:53 ` [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support M P
13 siblings, 0 replies; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
This adds support for creating an imx23 instance. This also contains
some of the more minor IO blocks, and a 'catchall' driver that helps
debugging access to undocumented IO registers.
Currently the instance can boot a linux kernel, but does not support
booting from the 'special' signed Freescale binary blobs.
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/arm/Makefile.objs | 2 +-
hw/arm/mxs.c | 388 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 389 insertions(+), 1 deletion(-)
create mode 100644 hw/arm/mxs.c
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index ea53988..45bbdb8 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -5,4 +5,4 @@ obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
obj-y += omap1.o omap2.o strongarm.o
-obj-$(CONFIG_MXS) += imx23_digctl.o imx23_pinctrl.o
+obj-$(CONFIG_MXS) += imx23_digctl.o imx23_pinctrl.o mxs.o
diff --git a/hw/arm/mxs.c b/hw/arm/mxs.c
new file mode 100644
index 0000000..bee7880
--- /dev/null
+++ b/hw/arm/mxs.c
@@ -0,0 +1,388 @@
+/*
+ * mxs.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ */
+
+#include "hw/sysbus.h"
+#include "hw/arm/mxs.h"
+#include "hw/arm/arm.h"
+#include "target-arm/cpu.h"
+#include "hw/boards.h"
+
+#include "exec/address-spaces.h"
+
+#define D(w)
+//#define D(w) w
+/*
+ * 0x00000000 - 0x00007fff On Chip SRAM
+ * - 0x5fffffff External DRAM
+ * 0x60000000 - 0x7fffffff Default Slave
+ * 0x80000000 - 0x800fffff Peripheral Space (128KB)
+ * 0x80000000 0x8000 APBH
+ * ----------------------------
+ * 0x80000000 0x2000 icol
+ * 0x80004000 0x2000 DMA
+ * 0x80008000 0x2000 ECC
+ * 0x8000c000 0x2000 GPMI-NAND
+ * 0x8000a000 0x2000 GPMI-NAND
+ * 0x80010000 0x2000 SSP0
+ * 0x80014000 0x2000 ETM
+ * 0x80018000 0x2000 pinctrl
+ * 0x8001c000 0x2000 digctl
+ * 0x80020000 0x2000 EMI
+ * 0x80024000 0x2000 DMA APBX
+ * 0x80028000 0x2000 DCP
+ * 0x8002a000 0x2000 PXP
+ * 0x8002c000 0x2000 ocotp
+ * 0x8002e000 0x2000 axi-ahb
+ * 0x80030000 0x2000 lcdif
+ * 0x80034000 0x2000 SSP1
+ * 0x80038000 0x2000 TVEnc
+ *
+ * 0x80040000 0x40000 APBX
+ * ----------------------------
+ * 0x80040000 0x2000 clkctrl
+ * 0x80042000 0x2000 saif0
+ * 0x80044000 0x2000 power
+ * 0x80046000 0x2000 saif1
+ * 0x80048000 0x2000 audio-out
+ * 0x8004c000 0x2000 audio-in
+ * 0x80050000 0x2000 LRADC
+ * 0x80054000 0x2000 SPDIF
+ * 0x80058000 0x2000 i2c
+ * 0x8005c000 0x2000 RTC fsl,imx23-rtc - fsl,stmp3xxx-rtc
+ * 0x80064000 0x2000 PWM
+ * 0x80068000 0x2000 Timrot
+ * 0x8006c000 0x2000 UART0
+ * 0x8006e000 0x2000 UART1
+ * 0x80070000 0x2000 DUART PL011
+ * 0x8007c000 0x2000 USB PHY
+ * 0x80100000 - 0xc0000000 Default Slave
+ * 0xc0000000 - 0xfffeffff ROM Alias
+ * 0xffff0000 - 0xffffffff On Chip ROM
+ */
+
+enum {
+ HW_CLKCTRL_CPU = 2,
+ HW_CLKCTRL_HBUS = 3,
+ HW_CLKCTRL_XBUS = 4,
+ HW_CLKCTRL_XTAL = 0x5,
+ HW_CLKCTRL_PIX = 0x6,
+ HW_CLKCTRL_SSP = 0x7,
+ HW_CLKCTRL_GPMI = 0x8,
+ HW_CLKCTRL_SPDIF = 0x9,
+ HW_CLKCTRL_EMI = 0xa,
+ HW_CLKCTRL_SAIF = 0xc,
+ HW_CLKCTRL_TV = 0xd,
+ HW_CLKCTRL_ETM = 0xe,
+ HW_CLKCTRL_FRAC = 0xf,
+ HW_CLKCTRL_FRAC1 = 0x10,
+ HW_CLKCTRL_CLKSEQ = 0x11,
+ HW_CLKCTRL_RESET = 0x12,
+ HW_CLKCTRL_STATUS = 0x13,
+ HW_CLKCTRL_VERSION = 0x14,
+ HW_CLKCTRL_MAX
+};
+typedef struct imx23_clkctrl_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ uint32_t r[HW_CLKCTRL_MAX];
+} imx23_clkctrl_state;
+
+static uint64_t imx23_clkctrl_read(
+ void *opaque, hwaddr offset, unsigned size)
+{
+ imx23_clkctrl_state *s = (imx23_clkctrl_state *) opaque;
+ uint32_t res = 0;
+
+ switch (offset >> 4) {
+ case 0 ... HW_CLKCTRL_MAX:
+ res = s->r[offset >> 4];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+ return res;
+}
+
+static void imx23_clkctrl_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ imx23_clkctrl_state *s = (imx23_clkctrl_state *) opaque;
+
+ switch (offset >> 4) {
+ case 0 ... HW_CLKCTRL_MAX:
+ if ((offset >> 4) == HW_CLKCTRL_RESET)
+ printf("QEMU: %s OS reset, ignored\n", __func__);
+ mxs_write(&s->r[offset >> 4], offset, value, size);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad offset 0x%x\n", __func__, (int) offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps imx23_clkctrl_ops = {
+ .read = imx23_clkctrl_read,
+ .write = imx23_clkctrl_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void imx23_clkctrl_reset(imx23_clkctrl_state *s)
+{
+ memset(s->r, 0, sizeof(s->r));
+ /*
+ * These are default values for most of the clock. the
+ * linux init code does rely on a few of these to be
+ * happy
+ */
+ s->r[HW_CLKCTRL_CPU] = 0x00010001;
+ s->r[HW_CLKCTRL_HBUS] = 0x00000001;
+ s->r[HW_CLKCTRL_XBUS] = 0x00000001;
+ s->r[HW_CLKCTRL_XTAL] = 0x70000001;
+ s->r[HW_CLKCTRL_PIX] = 0x80000001;
+ s->r[HW_CLKCTRL_SSP] = 0x80000001;
+ s->r[HW_CLKCTRL_GPMI] = 0x80000001;
+ s->r[HW_CLKCTRL_SPDIF] = 0x80000000;
+ s->r[HW_CLKCTRL_EMI] = 0x80000101;
+ s->r[HW_CLKCTRL_SAIF] = 0x80000001;
+ s->r[HW_CLKCTRL_TV] = 0x80000001;
+ s->r[HW_CLKCTRL_ETM] = 0x80000001;
+ s->r[HW_CLKCTRL_FRAC] = 0x92929292;
+ s->r[HW_CLKCTRL_FRAC1] = 0x80000000;
+ s->r[HW_CLKCTRL_CLKSEQ] = 0x0000001f;
+ s->r[HW_CLKCTRL_VERSION] = 0x04000000;
+}
+
+static int imx23_clkctrl_init(SysBusDevice *dev)
+{
+ imx23_clkctrl_state *s = OBJECT_CHECK(imx23_clkctrl_state, dev, "imx23_clkctrl");
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &imx23_clkctrl_ops, s,
+ "imx23_clkctrl", 0x2000);
+ sysbus_init_mmio(dev, &s->iomem);
+ imx23_clkctrl_reset(s);
+ return 0;
+}
+
+static void imx23_clkctrl_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = imx23_clkctrl_init;
+}
+
+static TypeInfo clkctrl_info = {
+ .name = "imx23_clkctrl",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(imx23_clkctrl_state),
+ .class_init = imx23_clkctrl_class_init,
+};
+
+static void imx23_clkctrl_register(void)
+{
+ type_register_static(&clkctrl_info);
+}
+
+type_init(imx23_clkctrl_register)
+
+/*
+ * The 'catchall' device block is partly for debugging purpose, and
+ * partly to sort out issues with 'lone registers' that are checked
+ * in blocks that appear to be outside dedicated peripheral space
+ *
+ * One such is the AMBA signature for the PL011 serial port, where
+ * linux relies of finding identifiers when qemu's pl011 doesn't reply
+ *
+ * Similartly, the USB block has a couple of "non EHCI compliant"
+ * registers that are needed to make the EHCI/imx driver happy
+ */
+typedef struct imx23_catchall_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+} imx23_catchall_state;
+
+static uint64_t imx23_catchall_read(
+ void *opaque, hwaddr offset, unsigned size)
+{
+ // imx23_catchall_state *s = (imx23_catchall_state *)opaque;
+ uint32_t res = 0;
+
+ // AMBA signature is not read by the pl11 serial driver, this is a workaround
+ const uint8_t cid[] = { 0x0d, 0xf0, 0x05, 0xb1 };
+ const uint8_t pid[] = { 0x11, 0x10, 0x34, 0x00 };
+ switch (offset) {
+ case 0x71fe0 ... 0x71fec:
+ res = pid[(offset - 0x71fe0) >> 2];
+ break;
+ case 0x71ff0 ... 0x71ffc:
+ res = cid[(offset - 0x71ff0) >> 2];
+ break;
+ case 0x80120: // HW_USBCTRL_DCIVERSION
+ res = 0x00000001;
+ break;
+ case 0x80124: // HW_USBCTRL_DCCPARAMS non ehci compliant
+ res = 0x00000185; // host & device bits
+ break;
+ default:
+ D(printf("%s %04x (%d) = ", __func__, (int) offset, size);
+ printf("%08x\n", res);)
+ break;
+ }
+ return res;
+}
+
+static void imx23_catchall_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ // imx23_catchall_state *s = (imx23_catchall_state *)opaque;
+
+ D(printf("%s %04x %08x(%d)\n", __func__, (int) offset, (int) value, size);)
+}
+
+static const MemoryRegionOps imx23_catchall_ops = {
+ .read = imx23_catchall_read,
+ .write = imx23_catchall_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int imx23_catchall_init(SysBusDevice *dev)
+{
+ imx23_catchall_state *s = OBJECT_CHECK(imx23_catchall_state, dev, "imx23_catchall");
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &imx23_catchall_ops, s,
+ "imx23_catchall", 0x82000);
+ sysbus_init_mmio(dev, &s->iomem);
+ return 0;
+}
+
+static void imx23_catchall_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+
+ sdc->init = imx23_catchall_init;
+}
+
+static TypeInfo catchall_info = {
+ .name = "imx23_catchall",
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(imx23_catchall_state),
+ .class_init = imx23_catchall_class_init,
+};
+
+static void imx23_catchall_register(void)
+{
+ type_register_static(&catchall_info);
+}
+
+type_init(imx23_catchall_register)
+
+ARMCPU * imx233_init(struct arm_boot_info * board_info);
+
+/*
+ * Creates an "empty" imx23, with the peripherals, and nothing
+ * else attached. Pass in a partially filled up board_info; currently
+ * only the ram_size field is used.
+ */
+ARMCPU * imx233_init(struct arm_boot_info * board_info)
+{
+ ARMCPU *cpu;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *ram = g_new(MemoryRegion, 1);
+// qemu_irq *cpu_pic;
+ DeviceState *icoll;
+
+ cpu = cpu_arm_init("arm926");
+ if (!cpu) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ /* On a real system, the first 32k is a `onboard sram' */
+ // printf("%s ram size : %dMB\n", __func__, (int)ram_size / 1024 / 1024);
+ memory_region_init_ram(ram, NULL, "imx233.ram", board_info->ram_size);
+ vmstate_register_ram_global(ram);
+ memory_region_add_subregion(address_space_mem, 0x0, ram);
+
+ sysbus_create_simple("imx23_catchall", MX23_IO_BASE_ADDR, 0);
+
+// cpu_pic = arm_pic_init_cpu(cpu);
+
+ sysbus_create_simple("imx23_clkctrl", MX23_CLKCTRL_BASE_ADDR, 0);
+
+ icoll = sysbus_create_varargs("mxs_icoll", MX23_ICOLL_BASE_ADDR,
+ qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ),
+ qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ), NULL);
+
+ sysbus_create_varargs("mxs_timrot", MX23_TIMROT_BASE_ADDR,
+ qdev_get_gpio_in(icoll, MX23_INT_TIMER0),
+ qdev_get_gpio_in(icoll, MX23_INT_TIMER1),
+ qdev_get_gpio_in(icoll, MX23_INT_TIMER2),
+ qdev_get_gpio_in(icoll, MX23_INT_TIMER3),
+ NULL);
+
+ sysbus_create_simple("imx23_digctl", MX23_DIGCTL_BASE_ADDR, 0);
+ sysbus_create_varargs("imx23_pinctrl", MX23_PINCTRL_BASE_ADDR,
+ qdev_get_gpio_in(icoll, MX23_INT_GPIO0),
+ qdev_get_gpio_in(icoll, MX23_INT_GPIO1),
+ qdev_get_gpio_in(icoll, MX23_INT_GPIO2),
+ NULL);
+
+ sysbus_create_simple("pl011", MX23_DUART_BASE_ADDR,
+ qdev_get_gpio_in(icoll, MX23_INT_DUART));
+ /*
+ * enable the port, like the bootloader would
+ */
+ {
+ uint32_t enable = 0x301;
+ cpu_physical_memory_rw(MX23_DUART_BASE_ADDR + 0x4 /* CR */,
+ (uint8_t*) &enable, 4, 1);
+ }
+ sysbus_create_varargs("mxs_uart", MX23_AUART1_BASE_ADDR,
+ qdev_get_gpio_in(icoll, MX23_INT_AUART1),
+ NULL);
+ sysbus_create_varargs("mxs_uart", MX23_AUART2_BASE_ADDR,
+ qdev_get_gpio_in(icoll, MX23_INT_AUART2),
+ NULL);
+ sysbus_create_varargs("mxs_rtc", MX23_RTC_BASE_ADDR,
+ qdev_get_gpio_in(icoll, MX23_INT_RTC_ALARM),
+ NULL);
+ sysbus_create_varargs("mxs_usb", MX23_USBCTRL_BASE_ADDR,
+ qdev_get_gpio_in(icoll, MX23_INT_USB_CTRL),
+ NULL);
+ sysbus_create_simple("mxs_usbphy", MX23_USBPHY_BASE_ADDR, 0);
+
+ sysbus_create_varargs("mxs_apbh_dma", MX23_APBH_DMA_BASE_ADDR,
+ qdev_get_gpio_in(icoll, MX23_INT_SSP1_DMA),
+ qdev_get_gpio_in(icoll, MX23_INT_SSP2_DMA),
+ NULL);
+ sysbus_create_varargs("mxs_apbx_dma", MX23_APBX_DMA_BASE_ADDR,
+ qdev_get_gpio_in(icoll, MX23_INT_ADC_DMA),
+ qdev_get_gpio_in(icoll, MX23_INT_DAC_DMA),
+ qdev_get_gpio_in(icoll, MX23_INT_SPDIF_DMA),
+ qdev_get_gpio_in(icoll, MX23_INT_I2C_DMA),
+ qdev_get_gpio_in(icoll, MX23_INT_SAIF1_DMA),
+ qdev_get_gpio_in(icoll, MX23_INT_AUART1_RX_DMA),
+ qdev_get_gpio_in(icoll, MX23_INT_AUART1_TX_DMA),
+ qdev_get_gpio_in(icoll, MX23_INT_AUART2_RX_DMA),
+ qdev_get_gpio_in(icoll, MX23_INT_AUART2_TX_DMA),
+ qdev_get_gpio_in(icoll, MX23_INT_SAIF2_DMA),
+ NULL);
+ sysbus_create_varargs("mxs_ssp", MX23_SSP1_BASE_ADDR,
+ qdev_get_gpio_in(icoll, MX23_INT_SSP1_DMA),
+ qdev_get_gpio_in(icoll, MX23_INT_SSP1_ERROR),
+ NULL);
+ sysbus_create_varargs("mxs_ssp", MX23_SSP2_BASE_ADDR,
+ qdev_get_gpio_in(icoll, MX23_INT_SSP2_DMA),
+ qdev_get_gpio_in(icoll, MX23_INT_SSP2_ERROR),
+ NULL);
+
+ return cpu;
+}
+
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* [Qemu-devel] [PATCH 13/13] mxs/imx23: Adds support for an Olinuxino board
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
` (11 preceding siblings ...)
2013-12-11 13:56 ` [Qemu-devel] [PATCH 12/13] mxs/imx23: Main core instantiation and minor IO blocks Michel Pollet
@ 2013-12-11 13:56 ` Michel Pollet
2013-12-13 12:53 ` [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support M P
13 siblings, 0 replies; 36+ messages in thread
From: Michel Pollet @ 2013-12-11 13:56 UTC (permalink / raw)
To: qemu-devel; +Cc: Michel Pollet
Adds support for creating a basic imx23 dev board from Olimex, with
a few peripherals, a bitbang i2c bus with a RTC attached, a DS18S20
thermal sensor, and a rather crude 'relay' that increases/decreases
the thermal sensor temperature.
Basicaly, it's a complete emulation of the hardware used for my real
life boiler controller system; but it's a nice starting point for
any other imx233 board prototyping.
https://plus.google.com/111387094029238541867/posts/Smwc7yFK3Vk
Signed-off-by: Michel Pollet <buserror@gmail.com>
---
hw/arm/Makefile.objs | 1 +
hw/arm/imx233-olinuxino.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 170 insertions(+)
create mode 100644 hw/arm/imx233-olinuxino.c
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 45bbdb8..d2bf180 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -6,3 +6,4 @@ obj-y += tosa.o versatilepb.o vexpress.o virt.o xilinx_zynq.o z2.o
obj-y += armv7m.o exynos4210.o pxa2xx.o pxa2xx_gpio.o pxa2xx_pic.o
obj-y += omap1.o omap2.o strongarm.o
obj-$(CONFIG_MXS) += imx23_digctl.o imx23_pinctrl.o mxs.o
+obj-$(CONFIG_MXS) += imx233-olinuxino.o
diff --git a/hw/arm/imx233-olinuxino.c b/hw/arm/imx233-olinuxino.c
new file mode 100644
index 0000000..3b1df95
--- /dev/null
+++ b/hw/arm/imx233-olinuxino.c
@@ -0,0 +1,169 @@
+/*
+ * imx233-olinuxino.c
+ *
+ * Copyright: Michel Pollet <buserror@gmail.com>
+ *
+ * QEMU Licence
+ *
+ * Support for a iMX233 development board. You can find reference for the
+ * olinuxino boards on Olimex's website at:
+ * https://www.olimex.com/Products/OLinuXino/iMX233/
+ *
+ * A typical instance of qemu can be created with the following command line:
+ ./arm-softmmu/qemu-system-arm -M imx233o -m 64M \
+ -serial stdio -display none \
+ -kernel /opt/minifs/build-imx233/vmlinuz-bare.dtb \
+ -monitor telnet::4444,server,nowait -s \
+ -sd /dev/loop0
+ The kernel command line can also be specified with -append. However the default
+ one should get a 3.x kernel booting with a working console.
+ */
+
+#include "hw/boards.h"
+#include "hw/arm/mxs.h"
+#include "hw/arm/arm.h"
+#include "hw/sysbus.h"
+#include "hw/i2c/bitbang_i2c.h"
+
+
+static struct arm_boot_info imx233o_binfo = {
+ /*
+ * theorically, the load address 0 is for the 'bootlets'
+ * however we don't support the bootlets yet, and the
+ * kernel is happy decompressing itself from 0x0 as well
+ * so it's not a big problem to start it from there.
+ */
+ .loader_start = 0x0,
+ .board_id = 4005, /* from linux's mach-types */
+ .is_linux = 1,
+};
+
+enum {
+ GPIO_SOFT_I2C_SDA = (0 * 32) + 25, // GPMI_RDN
+ GPIO_SOFT_I2C_SCL = (0 * 32) + 23, // GPMI_WPN
+
+ GPIO_W1 = (1 * 32) + 21,
+ GPIO_HEATER = 51,
+};
+
+typedef struct {
+ float temp;
+ int on;
+ qemu_irq set_temp;
+ qemu_irq * in;
+ QEMUTimer * timer;
+} GPIOHeater;
+
+static void gpio_heater_set(void *opaque, int irq, int level)
+{
+ GPIOHeater *h = opaque;
+ h->on = !level;
+ printf("QEMU %s %s\n", __func__, h->on ? "ON" : "OFF");
+}
+
+static void gpio_heater_timer(void *opaque)
+{
+ GPIOHeater *h = opaque;
+ if (h->on)
+ h->temp *= 1.001;
+ else
+ h->temp *= 0.9995;
+ if (h->temp < 10.0)
+ h->temp = 10.0;
+ else if (h->temp > 40)
+ h->temp = 40;
+// printf("QEMU %s %s %.2f\n", __func__, h->on ? "ON" : "OFF", h->temp);
+ qemu_set_irq(h->set_temp, (int)(h->temp * 1000.0f));
+ timer_mod(h->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
+}
+
+static int
+gpio_heater_init(GPIOHeater *h, qemu_irq set_temp)
+{
+ h->set_temp = set_temp;
+ h->temp = 13.0f;
+ h->on = 0;
+ h->in = qemu_allocate_irqs(gpio_heater_set, h, 1);
+ h->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, gpio_heater_timer, h);
+ timer_mod(h->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
+ return 0;
+}
+
+GPIOHeater heater;
+
+ARMCPU * imx233_init(struct arm_boot_info * board_info);
+
+static void imx233o_init(QEMUMachineInitArgs *args)
+{
+ struct arm_boot_info * board_info = &imx233o_binfo;
+ ARMCPU *cpu = NULL;
+
+ board_info->ram_size = ram_size;
+ board_info->kernel_filename = args->kernel_filename;
+ board_info->kernel_cmdline =
+ args->kernel_cmdline ?
+ args->kernel_cmdline :
+ "console=ttyAMA0,115200 ro root=/dev/mmcblk0p2 ssp1=mmc loglevel=7";
+ board_info->nb_cpus = 1;
+
+ cpu = imx233_init(board_info);
+
+ /*
+ * Recover the pin controller of the imx23.
+ * NOTE: that the device has to explicitly set it's 'name' for
+ * qdev_find_recursive() to work
+ */
+ DeviceState * gpio = qdev_find_recursive(sysbus_get_default(), "imx23_pinctrl");
+ /*
+ * Hook up a gpio-i2c bus to the pins that are reserved for that in
+ * the olinuxino .dts file, and add a RTC and an eeprom on it, because
+ * we can.
+ */
+ {
+ DeviceState * dev = sysbus_create_simple("gpio_i2c", -1, 0);
+
+ qdev_connect_gpio_out(gpio, GPIO_SOFT_I2C_SDA,
+ qdev_get_gpio_in(dev, BITBANG_I2C_SDA));
+ qdev_connect_gpio_out(dev, BITBANG_I2C_SDA,
+ qdev_get_gpio_in(gpio, GPIO_SOFT_I2C_SDA));
+
+ qdev_connect_gpio_out(gpio, GPIO_SOFT_I2C_SCL,
+ qdev_get_gpio_in(dev, BITBANG_I2C_SCL));
+
+ i2c_bus *bus = (i2c_bus *)qdev_get_child_bus(dev, "i2c");
+ printf("bus = %p\n", bus);
+ i2c_create_slave(bus, "ds1338", 0x68);
+ }
+ /*
+ * Add a onewire DS18S20 thermal sensor too. Theres one bidirectional GPIO
+ */
+ {
+ DeviceState * dev = sysbus_create_simple("ds18s20", -1, 0);
+
+ qdev_connect_gpio_out(gpio, GPIO_W1, qdev_get_gpio_in(dev, 0));
+ qdev_connect_gpio_out(dev, 0, qdev_get_gpio_in(gpio, GPIO_W1));
+
+ /*
+ * Hookup a virtual GPIO to a relay that change the
+ */
+ {
+ gpio_heater_init(&heater, qdev_get_gpio_in(dev, 1));
+ qdev_connect_gpio_out(gpio, GPIO_HEATER, heater.in[0]);
+ }
+ }
+ arm_load_kernel(cpu, board_info);
+
+}
+
+static QEMUMachine imx233o_machine = {
+ .name = "imx233o",
+ .desc = "i.MX233 Olinuxino (ARM926)",
+ .init = imx233o_init,
+};
+
+static void imx233o_machine_init(void)
+{
+ qemu_register_machine(&imx233o_machine);
+}
+
+machine_init(imx233o_machine_init)
--
1.8.5.1
^ permalink raw reply related [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support
2013-12-11 13:56 [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support Michel Pollet
` (12 preceding siblings ...)
2013-12-11 13:56 ` [Qemu-devel] [PATCH 13/13] mxs/imx23: Adds support for an Olinuxino board Michel Pollet
@ 2013-12-13 12:53 ` M P
2013-12-13 13:29 ` Peter Maydell
13 siblings, 1 reply; 36+ messages in thread
From: M P @ 2013-12-13 12:53 UTC (permalink / raw)
To: qemu-devel Developers; +Cc: Michel Pollet
[-- Attachment #1: Type: text/plain, Size: 3446 bytes --]
Can someone give me a pointer on how the review (if any) is done for these
patches? I have to say I'm rather amazed at the rate of submission on the
mailing list, and I worried to see these patches buried further and further
down in such a short timescale :-)
Michael
On Wed, Dec 11, 2013 at 1:56 PM, Michel Pollet <buserror@gmail.com> wrote:
> This series adds support for the imx233 SoC, and also adds support for
> emulating
> an Olinux Olinuxino board with a few peripherals, as a test harness.
> The emulation works pretty well, boots linux 3.12 vanilla from an emulated
> SD card,
> has USB bridge support (but no support for USB 1.1 devices like
> mouse+keyboard), RTC
> and quite a few other bits (some of them fairly skeletal)
>
> This series has been in used for quite a few months; it was posted here a
> few month
> back and one of the question was to wether I would stick around to support
> it.
> Perhaps the fact that I reworked it all on trunk and reposted it will help
> answer
> this question.
>
> This patch series is also available on this github branch, in case its'
> more
> convenient to use the inline comment function there.
> https://github.com/buserror-uk/qemu-buserror/commits/dev-imx233
>
>
> Michel Pollet (13):
> mxs/imx23: Add main header file
> mxs: Add CONFIG_MXS to the arm-softmmu config
> mxs/imx23: Add uart driver
> mxs/imx23: Add DMA driver
> mxs/imx23: Add the interrupt collector
> mxs/imx23: Add digctl driver
> mxs/imx23: Implements the pin mux, GPIOs
> mxs/imx23: Add SSP/SPI driver
> mxs/imx23: Add the RTC block
> mxs/imx23: Add the timers
> mxs/imx23: Add the USB driver
> mxs/imx23: Main core instantiation and minor IO blocks
> mxs/imx23: Adds support for an Olinuxino board
>
> default-configs/arm-softmmu.mak | 1 +
> hw/arm/Makefile.objs | 2 +
> hw/arm/imx233-olinuxino.c | 169 +++++++++++++++++
> hw/arm/imx23_digctl.c | 110 ++++++++++++
> hw/arm/imx23_pinctrl.c | 293 ++++++++++++++++++++++++++++++
> hw/arm/mxs.c | 388
> ++++++++++++++++++++++++++++++++++++++++
> hw/arm/mxs.h | 208 +++++++++++++++++++++
> hw/char/Makefile.objs | 1 +
> hw/char/mxs_uart.c | 146 +++++++++++++++
> hw/dma/Makefile.objs | 1 +
> hw/dma/mxs_dma.c | 347 +++++++++++++++++++++++++++++++++++
> hw/intc/Makefile.objs | 1 +
> hw/intc/mxs_icoll.c | 200 +++++++++++++++++++++
> hw/ssi/Makefile.objs | 1 +
> hw/ssi/mxs_spi.c | 239 +++++++++++++++++++++++++
> hw/timer/Makefile.objs | 1 +
> hw/timer/mxs_rtc.c | 147 +++++++++++++++
> hw/timer/mxs_timrot.c | 271 ++++++++++++++++++++++++++++
> hw/usb/Makefile.objs | 1 +
> hw/usb/mxs_usb.c | 254 ++++++++++++++++++++++++++
> 20 files changed, 2781 insertions(+)
> create mode 100644 hw/arm/imx233-olinuxino.c
> create mode 100644 hw/arm/imx23_digctl.c
> create mode 100644 hw/arm/imx23_pinctrl.c
> create mode 100644 hw/arm/mxs.c
> create mode 100644 hw/arm/mxs.h
> create mode 100644 hw/char/mxs_uart.c
> create mode 100644 hw/dma/mxs_dma.c
> create mode 100644 hw/intc/mxs_icoll.c
> create mode 100644 hw/ssi/mxs_spi.c
> create mode 100644 hw/timer/mxs_rtc.c
> create mode 100644 hw/timer/mxs_timrot.c
> create mode 100644 hw/usb/mxs_usb.c
>
> --
> 1.8.5.1
>
>
[-- Attachment #2: Type: text/html, Size: 4136 bytes --]
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support
2013-12-13 12:53 ` [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support M P
@ 2013-12-13 13:29 ` Peter Maydell
2013-12-13 13:45 ` M P
0 siblings, 1 reply; 36+ messages in thread
From: Peter Maydell @ 2013-12-13 13:29 UTC (permalink / raw)
To: M P; +Cc: qemu-devel Developers
On 13 December 2013 12:53, M P <buserror@gmail.com> wrote:
> Can someone give me a pointer on how the review (if any) is done for these
> patches? I have to say I'm rather amazed at the rate of submission on the
> mailing list, and I worried to see these patches buried further and further
> down in such a short timescale :-)
The rough process is:
* people reply to your email with review comments; when you've
accumulated enough you send out a fixed set of patches for
further review
* if nobody replies at all within say 2 weeks you can send out
a 'ping' followup to this coverletter to bring the set back
to peoples' attention
http://wiki.qemu.org/Contribute/SubmitAPatch is where we try
to describe how the process works.
In this particular case, I have tagged your mail in my mail
client as 'must-review'; however since my review queue is
currently pretty full (you're one of four new ARM board/soc
support patchsets, the others being Allwinner A10, Canon DIGIC
and RaspberryPi; then there's the TrustZone support patchset
and all the 64 bit support work) I'm afraid I can't currently
promise a particularly rapid turnaround time.
If you can find the mail threads relating to the other
recent board/soc support patchsets I listed above and
look through review comments to see if any of them would
apply to your board that would probably help.
thanks
-- PMM
^ permalink raw reply [flat|nested] 36+ messages in thread
* Re: [Qemu-devel] [PATCH 00/13] Freescale mxs/imx23 + Olimex Olinuxino support
2013-12-13 13:29 ` Peter Maydell
@ 2013-12-13 13:45 ` M P
0 siblings, 0 replies; 36+ messages in thread
From: M P @ 2013-12-13 13:45 UTC (permalink / raw)
To: Peter Maydell; +Cc: qemu-devel Developers
[-- Attachment #1: Type: text/plain, Size: 1770 bytes --]
Thanks Peter, very helpful -- and err, sorry to land you more work than you
already had :-)
Would I contribute anything by also reviewing and/or testing the sus
mentioned patches? I can at least test the A10 and raspi if this helps...
M
On Fri, Dec 13, 2013 at 1:29 PM, Peter Maydell <peter.maydell@linaro.org>wrote:
> On 13 December 2013 12:53, M P <buserror@gmail.com> wrote:
> > Can someone give me a pointer on how the review (if any) is done for
> these
> > patches? I have to say I'm rather amazed at the rate of submission on the
> > mailing list, and I worried to see these patches buried further and
> further
> > down in such a short timescale :-)
>
> The rough process is:
> * people reply to your email with review comments; when you've
> accumulated enough you send out a fixed set of patches for
> further review
> * if nobody replies at all within say 2 weeks you can send out
> a 'ping' followup to this coverletter to bring the set back
> to peoples' attention
>
> http://wiki.qemu.org/Contribute/SubmitAPatch is where we try
> to describe how the process works.
>
> In this particular case, I have tagged your mail in my mail
> client as 'must-review'; however since my review queue is
> currently pretty full (you're one of four new ARM board/soc
> support patchsets, the others being Allwinner A10, Canon DIGIC
> and RaspberryPi; then there's the TrustZone support patchset
> and all the 64 bit support work) I'm afraid I can't currently
> promise a particularly rapid turnaround time.
>
> If you can find the mail threads relating to the other
> recent board/soc support patchsets I listed above and
> look through review comments to see if any of them would
> apply to your board that would probably help.
>
> thanks
> -- PMM
>
[-- Attachment #2: Type: text/html, Size: 2435 bytes --]
^ permalink raw reply [flat|nested] 36+ messages in thread