123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- /*
- * i.MX USB PHY
- *
- * Copyright (c) 2020 Guenter Roeck <linux@roeck-us.net>
- *
- * 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/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)
|