diff --git a/MAINTAINERS b/MAINTAINERS index 32867bc636..f8741cabdc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -764,6 +764,8 @@ F: hw/arm/sabrelite.c F: hw/arm/fsl-imx6.c F: hw/misc/imx6_*.c F: hw/ssi/imx_spi.c +F: hw/usb/imx-usb-phy.c +F: include/hw/usb/imx-usb-phy.h F: include/hw/arm/fsl-imx6.h F: include/hw/misc/imx6_*.h F: include/hw/ssi/imx_spi.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index e5a876c8d1..188419dc1e 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -373,6 +373,7 @@ config FSL_IMX6 select IMX select IMX_FEC select IMX_I2C + select IMX_USBPHY select SDHCI config ASPEED_SOC diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index ecc62855f2..e095e4abc6 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -22,6 +22,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/arm/fsl-imx6.h" +#include "hw/usb/imx-usb-phy.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "sysemu/sysemu.h" @@ -86,6 +87,17 @@ static void fsl_imx6_init(Object *obj) TYPE_IMX_USDHC); } + for (i = 0; i < FSL_IMX6_NUM_USB_PHYS; i++) { + snprintf(name, NAME_SIZE, "usbphy%d", i); + sysbus_init_child_obj(obj, name, &s->usbphy[i], sizeof(s->usbphy[i]), + TYPE_IMX_USBPHY); + } + for (i = 0; i < FSL_IMX6_NUM_USBS; i++) { + snprintf(name, NAME_SIZE, "usb%d", i); + sysbus_init_child_obj(obj, name, &s->usb[i], sizeof(s->usb[i]), + TYPE_CHIPIDEA); + } + for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) { snprintf(name, NAME_SIZE, "spi%d", i + 1); sysbus_init_child_obj(obj, name, &s->spi[i], sizeof(s->spi[i]), @@ -349,6 +361,30 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) esdhc_table[i].irq)); } + /* USB */ + for (i = 0; i < FSL_IMX6_NUM_USB_PHYS; i++) { + object_property_set_bool(OBJECT(&s->usbphy[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usbphy[i]), 0, + FSL_IMX6_USBPHY1_ADDR + i * 0x1000); + } + for (i = 0; i < FSL_IMX6_NUM_USBS; i++) { + static const int FSL_IMX6_USBn_IRQ[] = { + FSL_IMX6_USB_OTG_IRQ, + FSL_IMX6_USB_HOST1_IRQ, + FSL_IMX6_USB_HOST2_IRQ, + FSL_IMX6_USB_HOST3_IRQ, + }; + + object_property_set_bool(OBJECT(&s->usb[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, + FSL_IMX6_USBOH3_USB_ADDR + i * 0x200); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a9mpcore), + FSL_IMX6_USBn_IRQ[i])); + } + /* Initialize all ECSPI */ for (i = 0; i < FSL_IMX6_NUM_ECSPIS; i++) { static const struct { diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index c405b68d1d..99a5859a4e 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -20,6 +20,7 @@ #include "qapi/error.h" #include "hw/arm/fsl-imx6ul.h" #include "hw/misc/unimp.h" +#include "hw/usb/imx-usb-phy.h" #include "hw/boards.h" #include "sysemu/sysemu.h" #include "qemu/error-report.h" @@ -133,6 +134,18 @@ static void fsl_imx6ul_init(Object *obj) TYPE_IMX_ENET); } + /* USB */ + for (i = 0; i < FSL_IMX6UL_NUM_USB_PHYS; i++) { + snprintf(name, NAME_SIZE, "usbphy%d", i); + sysbus_init_child_obj(obj, name, &s->usbphy[i], sizeof(s->usbphy[i]), + TYPE_IMX_USBPHY); + } + for (i = 0; i < FSL_IMX6UL_NUM_USBS; i++) { + snprintf(name, NAME_SIZE, "usb%d", i); + sysbus_init_child_obj(obj, name, &s->usb[i], sizeof(s->usb[i]), + TYPE_CHIPIDEA); + } + /* * SDHCI */ @@ -456,6 +469,28 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_ENETn_TIMER_IRQ[i])); } + /* USB */ + for (i = 0; i < FSL_IMX6UL_NUM_USB_PHYS; i++) { + object_property_set_bool(OBJECT(&s->usbphy[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usbphy[i]), 0, + FSL_IMX6UL_USBPHY1_ADDR + i * 0x1000); + } + + for (i = 0; i < FSL_IMX6UL_NUM_USBS; i++) { + static const int FSL_IMX6UL_USBn_IRQ[] = { + FSL_IMX6UL_USB1_IRQ, + FSL_IMX6UL_USB2_IRQ, + }; + object_property_set_bool(OBJECT(&s->usb[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, + FSL_IMX6UL_USBO2_USB_ADDR + i * 0x200); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_USBn_IRQ[i])); + } + /* * USDHC */ @@ -516,6 +551,20 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) */ create_unimplemented_device("sdma", FSL_IMX6UL_SDMA_ADDR, 0x4000); + /* + * PWM + */ + create_unimplemented_device("pwm1", FSL_IMX6UL_PWM1_ADDR, 0x4000); + create_unimplemented_device("pwm2", FSL_IMX6UL_PWM2_ADDR, 0x4000); + create_unimplemented_device("pwm3", FSL_IMX6UL_PWM3_ADDR, 0x4000); + create_unimplemented_device("pwm4", FSL_IMX6UL_PWM4_ADDR, 0x4000); + + /* + * CAN + */ + create_unimplemented_device("can1", FSL_IMX6UL_CAN1_ADDR, 0x4000); + create_unimplemented_device("can2", FSL_IMX6UL_CAN2_ADDR, 0x4000); + /* * APHB_DMA */ diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 56a36202d7..336c9bad4a 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -2290,9 +2290,6 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) s->ssp[i] = (SSIBus *)qdev_get_child_bus(dev, "ssi"); } - sysbus_create_simple("sysbus-ohci", 0x4c000000, - qdev_get_gpio_in(s->pic, PXA2XX_PIC_USBH1)); - s->pcmcia[0] = pxa2xx_pcmcia_init(address_space, 0x20000000); s->pcmcia[1] = pxa2xx_pcmcia_init(address_space, 0x30000000); diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 61f2fb8f8f..8227088441 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -32,17 +32,7 @@ #include "qemu/module.h" #include "qemu/error-report.h" #include "qapi/error.h" - -#ifndef M25P80_ERR_DEBUG -#define M25P80_ERR_DEBUG 0 -#endif - -#define DB_PRINT_L(level, ...) do { \ - if (M25P80_ERR_DEBUG > (level)) { \ - fprintf(stderr, ": %s: ", __func__); \ - fprintf(stderr, ## __VA_ARGS__); \ - } \ -} while (0) +#include "trace.h" /* Fields for FlashPartInfo->flags */ @@ -574,7 +564,8 @@ static void flash_erase(Flash *s, int offset, FlashCMD cmd) abort(); } - DB_PRINT_L(0, "offset = %#x, len = %d\n", offset, len); + trace_m25p80_flash_erase(s, offset, len); + if ((s->pi->flags & capa_to_assert) != capa_to_assert) { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: %d erase size not supported by" " device\n", len); @@ -607,8 +598,7 @@ void flash_write8(Flash *s, uint32_t addr, uint8_t data) } if ((prev ^ data) & data) { - DB_PRINT_L(1, "programming zero to one! addr=%" PRIx32 " %" PRIx8 - " -> %" PRIx8 "\n", addr, prev, data); + trace_m25p80_programming_zero_to_one(s, addr, prev, data); } if (s->pi->flags & EEPROM) { @@ -662,6 +652,9 @@ static void complete_collecting_data(Flash *s) s->state = STATE_IDLE; + trace_m25p80_complete_collecting(s, s->cmd_in_progress, n, s->ear, + s->cur_addr); + switch (s->cmd_in_progress) { case DPP: case QPP: @@ -825,7 +818,7 @@ static void reset_memory(Flash *s) break; } - DB_PRINT_L(0, "Reset done.\n"); + trace_m25p80_reset_done(s); } static void decode_fast_read_cmd(Flash *s) @@ -941,9 +934,10 @@ static void decode_qio_read_cmd(Flash *s) static void decode_new_cmd(Flash *s, uint32_t value) { - s->cmd_in_progress = value; int i; - DB_PRINT_L(0, "decoded new command:%x\n", value); + + s->cmd_in_progress = value; + trace_m25p80_command_decoded(s, value); if (value != RESET_MEMORY) { s->reset_enable = false; @@ -1042,12 +1036,15 @@ static void decode_new_cmd(Flash *s, uint32_t value) break; case JEDEC_READ: - DB_PRINT_L(0, "populated jedec code\n"); + trace_m25p80_populated_jedec(s); for (i = 0; i < s->pi->id_len; i++) { s->data[i] = s->pi->id[i]; } + for (; i < SPI_NOR_MAX_ID_LEN; i++) { + s->data[i] = 0; + } - s->len = s->pi->id_len; + s->len = SPI_NOR_MAX_ID_LEN; s->pos = 0; s->state = STATE_READING_DATA; break; @@ -1063,7 +1060,7 @@ static void decode_new_cmd(Flash *s, uint32_t value) case BULK_ERASE_60: case BULK_ERASE: if (s->write_enable) { - DB_PRINT_L(0, "chip erase\n"); + trace_m25p80_chip_erase(s); flash_erase(s, 0, BULK_ERASE); } else { qemu_log_mask(LOG_GUEST_ERROR, "M25P80: chip erase with write " @@ -1164,6 +1161,11 @@ static void decode_new_cmd(Flash *s, uint32_t value) s->quad_enable = false; break; default: + s->pos = 0; + s->len = 1; + s->state = STATE_READING_DATA; + s->data_read_loop = true; + s->data[0] = 0; qemu_log_mask(LOG_GUEST_ERROR, "M25P80: Unknown cmd %x\n", value); break; } @@ -1184,7 +1186,7 @@ static int m25p80_cs(SSISlave *ss, bool select) s->data_read_loop = false; } - DB_PRINT_L(0, "%sselect\n", select ? "de" : ""); + trace_m25p80_select(s, select ? "de" : ""); return 0; } @@ -1194,19 +1196,20 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) Flash *s = M25P80(ss); uint32_t r = 0; + trace_m25p80_transfer(s, s->state, s->len, s->needed_bytes, s->pos, + s->cur_addr, (uint8_t)tx); + switch (s->state) { case STATE_PAGE_PROGRAM: - DB_PRINT_L(1, "page program cur_addr=%#" PRIx32 " data=%" PRIx8 "\n", - s->cur_addr, (uint8_t)tx); + trace_m25p80_page_program(s, s->cur_addr, (uint8_t)tx); flash_write8(s, s->cur_addr, (uint8_t)tx); s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; case STATE_READ: r = s->storage[s->cur_addr]; - DB_PRINT_L(1, "READ 0x%" PRIx32 "=%" PRIx8 "\n", s->cur_addr, - (uint8_t)r); + trace_m25p80_read_byte(s, s->cur_addr, (uint8_t)r); s->cur_addr = (s->cur_addr + 1) & (s->size - 1); break; @@ -1244,6 +1247,7 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx) } r = s->data[s->pos]; + trace_m25p80_read_data(s, s->pos, (uint8_t)r); s->pos++; if (s->pos == s->len) { s->pos = 0; @@ -1281,7 +1285,7 @@ static void m25p80_realize(SSISlave *ss, Error **errp) return; } - DB_PRINT_L(0, "Binding to IF_MTD drive\n"); + trace_m25p80_binding(s); s->storage = blk_blockalign(s->blk, s->size); if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { @@ -1289,7 +1293,7 @@ static void m25p80_realize(SSISlave *ss, Error **errp) return; } } else { - DB_PRINT_L(0, "No BDRV - binding to RAM\n"); + trace_m25p80_binding_no_bdrv(s); s->storage = blk_blockalign(NULL, s->size); memset(s->storage, 0xFF, s->size); } diff --git a/hw/block/trace-events b/hw/block/trace-events index c03e80c2c9..f78939fa9d 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -134,3 +134,19 @@ xen_block_blockdev_add(char *str) "%s" xen_block_blockdev_del(const char *node_name) "%s" xen_block_device_create(unsigned int number) "%u" xen_block_device_destroy(unsigned int number) "%u" + +# m25p80.c +m25p80_flash_erase(void *s, int offset, uint32_t len) "[%p] offset = 0x%"PRIx32", len = %u" +m25p80_programming_zero_to_one(void *s, uint32_t addr, uint8_t prev, uint8_t data) "[%p] programming zero to one! addr=0x%"PRIx32" 0x%"PRIx8" -> 0x%"PRIx8 +m25p80_reset_done(void *s) "[%p] Reset done." +m25p80_command_decoded(void *s, uint32_t cmd) "[%p] new command:0x%"PRIx32 +m25p80_complete_collecting(void *s, uint32_t cmd, int n, uint8_t ear, uint32_t cur_addr) "[%p] decode cmd: 0x%"PRIx32" len %d ear 0x%"PRIx8" addr 0x%"PRIx32 +m25p80_populated_jedec(void *s) "[%p] populated jedec code" +m25p80_chip_erase(void *s) "[%p] chip erase" +m25p80_select(void *s, const char *what) "[%p] %sselect" +m25p80_page_program(void *s, uint32_t addr, uint8_t tx) "[%p] page program cur_addr=0x%"PRIx32" data=0x%"PRIx8 +m25p80_transfer(void *s, uint8_t state, uint32_t len, uint8_t needed, uint32_t pos, uint32_t cur_addr, uint8_t t) "[%p] Transfer state 0x%"PRIx8" len 0x%"PRIx32" needed 0x%"PRIx8" pos 0x%"PRIx32" addr 0x%"PRIx32" tx 0x%"PRIx8 +m25p80_read_byte(void *s, uint32_t addr, uint8_t v) "[%p] Read byte 0x%"PRIx32"=0x%"PRIx8 +m25p80_read_data(void *s, uint32_t pos, uint8_t v) "[%p] Read data 0x%"PRIx32"=0x%"PRIx8 +m25p80_binding(void *s) "[%p] Binding to IF_MTD drive" +m25p80_binding_no_bdrv(void *s) "[%p] No BDRV - binding to RAM" diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 6a124a154a..5c145a8197 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -855,13 +855,15 @@ static void imx_enet_write(IMXFECState *s, uint32_t index, uint32_t value) break; case ENET_TGSR: /* implement clear timer flag */ - value = value & 0x0000000f; + s->regs[index] &= ~(value & 0x0000000f); /* all bits W1C */ break; case ENET_TCSR0: case ENET_TCSR1: case ENET_TCSR2: case ENET_TCSR3: - value = value & 0x000000fd; + s->regs[index] &= ~(value & 0x00000080); /* W1C bits */ + s->regs[index] &= ~0x0000007d; /* writable fields */ + s->regs[index] |= (value & 0x0000007d); break; case ENET_TCCR0: case ENET_TCCR1: diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 32be2a02b0..9d5c696d5a 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -788,11 +788,11 @@ static int aspeed_smc_num_dummies(uint8_t command) case FAST_READ: case DOR: case QOR: + case FAST_READ_4: case DOR_4: case QOR_4: return 1; case DIOR: - case FAST_READ_4: case DIOR_4: return 2; case QIOR: diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig index 5e70ed5f7b..464348ba14 100644 --- a/hw/usb/Kconfig +++ b/hw/usb/Kconfig @@ -91,3 +91,8 @@ config USB_STORAGE_MTP bool default y depends on USB + +config IMX_USBPHY + bool + default y + depends on USB diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 2b10868937..66835e5bf7 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -61,3 +61,5 @@ common-obj-$(CONFIG_XEN) += xen-usb.o xen-usb.o-cflags := $(LIBUSB_CFLAGS) xen-usb.o-libs := $(LIBUSB_LIBS) endif + +common-obj-$(CONFIG_IMX_USBPHY) += imx-usb-phy.o diff --git a/hw/usb/imx-usb-phy.c b/hw/usb/imx-usb-phy.c new file mode 100644 index 0000000000..e705a03a1f --- /dev/null +++ b/hw/usb/imx-usb-phy.c @@ -0,0 +1,225 @@ +/* + * i.MX USB PHY + * + * Copyright (c) 2020 Guenter Roeck + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * We need to implement basic reset control in the PHY control register. + * For everything else, it is sufficient to set whatever is written. + */ + +#include "qemu/osdep.h" +#include "hw/usb/imx-usb-phy.h" +#include "migration/vmstate.h" +#include "qemu/log.h" +#include "qemu/module.h" + +static const VMStateDescription vmstate_imx_usbphy = { + .name = TYPE_IMX_USBPHY, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(usbphy, IMXUSBPHYState, USBPHY_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static void imx_usbphy_softreset(IMXUSBPHYState *s) +{ + s->usbphy[USBPHY_PWD] = 0x001e1c00; + s->usbphy[USBPHY_TX] = 0x10060607; + s->usbphy[USBPHY_RX] = 0x00000000; + s->usbphy[USBPHY_CTRL] = 0xc0200000; +} + +static void imx_usbphy_reset(DeviceState *dev) +{ + IMXUSBPHYState *s = IMX_USBPHY(dev); + + s->usbphy[USBPHY_STATUS] = 0x00000000; + s->usbphy[USBPHY_DEBUG] = 0x7f180000; + s->usbphy[USBPHY_DEBUG0_STATUS] = 0x00000000; + s->usbphy[USBPHY_DEBUG1] = 0x00001000; + s->usbphy[USBPHY_VERSION] = 0x04020000; + + imx_usbphy_softreset(s); +} + +static uint64_t imx_usbphy_read(void *opaque, hwaddr offset, unsigned size) +{ + IMXUSBPHYState *s = (IMXUSBPHYState *)opaque; + uint32_t index = offset >> 2; + uint32_t value; + + switch (index) { + case USBPHY_PWD_SET: + case USBPHY_TX_SET: + case USBPHY_RX_SET: + case USBPHY_CTRL_SET: + case USBPHY_DEBUG_SET: + case USBPHY_DEBUG1_SET: + /* + * All REG_NAME_SET register access are in fact targeting the + * REG_NAME register. + */ + value = s->usbphy[index - 1]; + break; + case USBPHY_PWD_CLR: + case USBPHY_TX_CLR: + case USBPHY_RX_CLR: + case USBPHY_CTRL_CLR: + case USBPHY_DEBUG_CLR: + case USBPHY_DEBUG1_CLR: + /* + * All REG_NAME_CLR register access are in fact targeting the + * REG_NAME register. + */ + value = s->usbphy[index - 2]; + break; + case USBPHY_PWD_TOG: + case USBPHY_TX_TOG: + case USBPHY_RX_TOG: + case USBPHY_CTRL_TOG: + case USBPHY_DEBUG_TOG: + case USBPHY_DEBUG1_TOG: + /* + * All REG_NAME_TOG register access are in fact targeting the + * REG_NAME register. + */ + value = s->usbphy[index - 3]; + break; + default: + value = s->usbphy[index]; + break; + } + return (uint64_t)value; +} + +static void imx_usbphy_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + IMXUSBPHYState *s = (IMXUSBPHYState *)opaque; + uint32_t index = offset >> 2; + + switch (index) { + case USBPHY_CTRL: + s->usbphy[index] = value; + if (value & USBPHY_CTRL_SFTRST) { + imx_usbphy_softreset(s); + } + break; + case USBPHY_PWD: + case USBPHY_TX: + case USBPHY_RX: + case USBPHY_STATUS: + case USBPHY_DEBUG: + case USBPHY_DEBUG1: + s->usbphy[index] = value; + break; + case USBPHY_CTRL_SET: + s->usbphy[index - 1] |= value; + if (value & USBPHY_CTRL_SFTRST) { + imx_usbphy_softreset(s); + } + break; + case USBPHY_PWD_SET: + case USBPHY_TX_SET: + case USBPHY_RX_SET: + case USBPHY_DEBUG_SET: + case USBPHY_DEBUG1_SET: + /* + * All REG_NAME_SET register access are in fact targeting the + * REG_NAME register. So we change the value of the REG_NAME + * register, setting bits passed in the value. + */ + s->usbphy[index - 1] |= value; + break; + case USBPHY_PWD_CLR: + case USBPHY_TX_CLR: + case USBPHY_RX_CLR: + case USBPHY_CTRL_CLR: + case USBPHY_DEBUG_CLR: + case USBPHY_DEBUG1_CLR: + /* + * All REG_NAME_CLR register access are in fact targeting the + * REG_NAME register. So we change the value of the REG_NAME + * register, unsetting bits passed in the value. + */ + s->usbphy[index - 2] &= ~value; + break; + case USBPHY_CTRL_TOG: + s->usbphy[index - 3] ^= value; + if ((value & USBPHY_CTRL_SFTRST) && + (s->usbphy[index - 3] & USBPHY_CTRL_SFTRST)) { + imx_usbphy_softreset(s); + } + break; + case USBPHY_PWD_TOG: + case USBPHY_TX_TOG: + case USBPHY_RX_TOG: + case USBPHY_DEBUG_TOG: + case USBPHY_DEBUG1_TOG: + /* + * All REG_NAME_TOG register access are in fact targeting the + * REG_NAME register. So we change the value of the REG_NAME + * register, toggling bits passed in the value. + */ + s->usbphy[index - 3] ^= value; + break; + default: + /* Other registers are read-only */ + break; + } +} + +static const struct MemoryRegionOps imx_usbphy_ops = { + .read = imx_usbphy_read, + .write = imx_usbphy_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + /* + * Our device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .min_access_size = 4, + .max_access_size = 4, + .unaligned = false, + }, +}; + +static void imx_usbphy_realize(DeviceState *dev, Error **errp) +{ + IMXUSBPHYState *s = IMX_USBPHY(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), &imx_usbphy_ops, s, + "imx-usbphy", 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); +} + +static void imx_usbphy_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = imx_usbphy_reset; + dc->vmsd = &vmstate_imx_usbphy; + dc->desc = "i.MX USB PHY Module"; + dc->realize = imx_usbphy_realize; +} + +static const TypeInfo imx_usbphy_info = { + .name = TYPE_IMX_USBPHY, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IMXUSBPHYState), + .class_init = imx_usbphy_class_init, +}; + +static void imx_usbphy_register_types(void) +{ + type_register_static(&imx_usbphy_info); +} + +type_init(imx_usbphy_register_types) diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h index 60eadccb42..973bcb72f7 100644 --- a/include/hw/arm/fsl-imx6.h +++ b/include/hw/arm/fsl-imx6.h @@ -30,6 +30,8 @@ #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" #include "hw/net/imx_fec.h" +#include "hw/usb/chipidea.h" +#include "hw/usb/imx-usb-phy.h" #include "exec/memory.h" #include "cpu.h" @@ -44,6 +46,8 @@ #define FSL_IMX6_NUM_ESDHCS 4 #define FSL_IMX6_NUM_ECSPIS 5 #define FSL_IMX6_NUM_WDTS 2 +#define FSL_IMX6_NUM_USB_PHYS 2 +#define FSL_IMX6_NUM_USBS 4 typedef struct FslIMX6State { /*< private >*/ @@ -62,6 +66,8 @@ typedef struct FslIMX6State { SDHCIState esdhc[FSL_IMX6_NUM_ESDHCS]; IMXSPIState spi[FSL_IMX6_NUM_ECSPIS]; IMX2WdtState wdt[FSL_IMX6_NUM_WDTS]; + IMXUSBPHYState usbphy[FSL_IMX6_NUM_USB_PHYS]; + ChipideaState usb[FSL_IMX6_NUM_USBS]; IMXFECState eth; MemoryRegion rom; MemoryRegion caam; diff --git a/include/hw/arm/fsl-imx6ul.h b/include/hw/arm/fsl-imx6ul.h index eda389aec7..1a0bab8daa 100644 --- a/include/hw/arm/fsl-imx6ul.h +++ b/include/hw/arm/fsl-imx6ul.h @@ -34,6 +34,8 @@ #include "hw/sd/sdhci.h" #include "hw/ssi/imx_spi.h" #include "hw/net/imx_fec.h" +#include "hw/usb/chipidea.h" +#include "hw/usb/imx-usb-phy.h" #include "exec/memory.h" #include "cpu.h" @@ -54,6 +56,8 @@ enum FslIMX6ULConfiguration { FSL_IMX6UL_NUM_I2CS = 4, FSL_IMX6UL_NUM_ECSPIS = 4, FSL_IMX6UL_NUM_ADCS = 2, + FSL_IMX6UL_NUM_USB_PHYS = 2, + FSL_IMX6UL_NUM_USBS = 2, }; typedef struct FslIMX6ULState { @@ -77,6 +81,8 @@ typedef struct FslIMX6ULState { IMXFECState eth[FSL_IMX6UL_NUM_ETHS]; SDHCIState usdhc[FSL_IMX6UL_NUM_USDHCS]; IMX2WdtState wdt[FSL_IMX6UL_NUM_WDTS]; + IMXUSBPHYState usbphy[FSL_IMX6UL_NUM_USB_PHYS]; + ChipideaState usb[FSL_IMX6UL_NUM_USBS]; MemoryRegion rom; MemoryRegion caam; MemoryRegion ocram; @@ -145,6 +151,10 @@ enum FslIMX6ULMemoryMap { FSL_IMX6UL_EPIT2_ADDR = 0x020D4000, FSL_IMX6UL_EPIT1_ADDR = 0x020D0000, FSL_IMX6UL_SNVS_HP_ADDR = 0x020CC000, + FSL_IMX6UL_USBPHY2_ADDR = 0x020CA000, + FSL_IMX6UL_USBPHY2_SIZE = (4 * 1024), + FSL_IMX6UL_USBPHY1_ADDR = 0x020C9000, + FSL_IMX6UL_USBPHY1_SIZE = (4 * 1024), FSL_IMX6UL_ANALOG_ADDR = 0x020C8000, FSL_IMX6UL_CCM_ADDR = 0x020C4000, FSL_IMX6UL_WDOG2_ADDR = 0x020C0000, @@ -241,10 +251,10 @@ enum FslIMX6ULIRQs { FSL_IMX6UL_UART7_IRQ = 39, FSL_IMX6UL_UART8_IRQ = 40, - FSL_IMX6UL_USB1_IRQ = 42, - FSL_IMX6UL_USB2_IRQ = 43, + FSL_IMX6UL_USB1_IRQ = 43, + FSL_IMX6UL_USB2_IRQ = 42, FSL_IMX6UL_USB_PHY1_IRQ = 44, - FSL_IMX6UL_USB_PHY2_IRQ = 44, + FSL_IMX6UL_USB_PHY2_IRQ = 45, FSL_IMX6UL_CAAM_JQ2_IRQ = 46, FSL_IMX6UL_CAAM_ERR_IRQ = 47, diff --git a/include/hw/usb/imx-usb-phy.h b/include/hw/usb/imx-usb-phy.h new file mode 100644 index 0000000000..07f0235d10 --- /dev/null +++ b/include/hw/usb/imx-usb-phy.h @@ -0,0 +1,53 @@ +#ifndef IMX_USB_PHY_H +#define IMX_USB_PHY_H + +#include "hw/sysbus.h" +#include "qemu/bitops.h" + +enum IMXUsbPhyRegisters { + USBPHY_PWD, + USBPHY_PWD_SET, + USBPHY_PWD_CLR, + USBPHY_PWD_TOG, + USBPHY_TX, + USBPHY_TX_SET, + USBPHY_TX_CLR, + USBPHY_TX_TOG, + USBPHY_RX, + USBPHY_RX_SET, + USBPHY_RX_CLR, + USBPHY_RX_TOG, + USBPHY_CTRL, + USBPHY_CTRL_SET, + USBPHY_CTRL_CLR, + USBPHY_CTRL_TOG, + USBPHY_STATUS, + USBPHY_DEBUG = 0x14, + USBPHY_DEBUG_SET, + USBPHY_DEBUG_CLR, + USBPHY_DEBUG_TOG, + USBPHY_DEBUG0_STATUS, + USBPHY_DEBUG1 = 0x1c, + USBPHY_DEBUG1_SET, + USBPHY_DEBUG1_CLR, + USBPHY_DEBUG1_TOG, + USBPHY_VERSION, + USBPHY_MAX +}; + +#define USBPHY_CTRL_SFTRST BIT(31) + +#define TYPE_IMX_USBPHY "imx.usbphy" +#define IMX_USBPHY(obj) OBJECT_CHECK(IMXUSBPHYState, (obj), TYPE_IMX_USBPHY) + +typedef struct IMXUSBPHYState { + /* */ + SysBusDevice parent_obj; + + /* */ + MemoryRegion iomem; + + uint32_t usbphy[USBPHY_MAX]; +} IMXUSBPHYState; + +#endif /* IMX_USB_PHY_H */