| 1 | /* |
| 2 | * i.MX USB PHY |
| 3 | * |
| 4 | * Copyright (c) 2020 Guenter Roeck <linux@roeck-us.net> |
| 5 | * |
| 6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| 7 | * See the COPYING file in the top-level directory. |
| 8 | * |
| 9 | * We need to implement basic reset control in the PHY control register. |
| 10 | * For everything else, it is sufficient to set whatever is written. |
| 11 | */ |
| 12 | |
| 13 | #include "qemu/osdep.h" |
| 14 | #include "hw/usb/imx-usb-phy.h" |
| 15 | #include "migration/vmstate.h" |
| 16 | #include "qemu/log.h" |
| 17 | #include "qemu/module.h" |
| 18 | |
| 19 | static const VMStateDescription vmstate_imx_usbphy = { |
| 20 | .name = TYPE_IMX_USBPHY, |
| 21 | .version_id = 1, |
| 22 | .minimum_version_id = 1, |
| 23 | .fields = (const VMStateField[]) { |
| 24 | VMSTATE_UINT32_ARRAY(usbphy, IMXUSBPHYState, USBPHY_MAX), |
| 25 | VMSTATE_END_OF_LIST() |
| 26 | }, |
| 27 | }; |
| 28 | |
| 29 | static void imx_usbphy_softreset(IMXUSBPHYState *s) |
| 30 | { |
| 31 | s->usbphy[USBPHY_PWD] = 0x001e1c00; |
| 32 | s->usbphy[USBPHY_TX] = 0x10060607; |
| 33 | s->usbphy[USBPHY_RX] = 0x00000000; |
| 34 | s->usbphy[USBPHY_CTRL] = 0xc0200000; |
| 35 | } |
| 36 | |
| 37 | static void imx_usbphy_reset(DeviceState *dev) |
| 38 | { |
| 39 | IMXUSBPHYState *s = IMX_USBPHY(dev); |
| 40 | |
| 41 | s->usbphy[USBPHY_STATUS] = 0x00000000; |
| 42 | s->usbphy[USBPHY_DEBUG] = 0x7f180000; |
| 43 | s->usbphy[USBPHY_DEBUG0_STATUS] = 0x00000000; |
| 44 | s->usbphy[USBPHY_DEBUG1] = 0x00001000; |
| 45 | s->usbphy[USBPHY_VERSION] = 0x04020000; |
| 46 | |
| 47 | imx_usbphy_softreset(s); |
| 48 | } |
| 49 | |
| 50 | static uint64_t imx_usbphy_read(void *opaque, hwaddr offset, unsigned size) |
| 51 | { |
| 52 | IMXUSBPHYState *s = (IMXUSBPHYState *)opaque; |
| 53 | uint32_t index = offset >> 2; |
| 54 | uint32_t value; |
| 55 | |
| 56 | switch (index) { |
| 57 | case USBPHY_PWD_SET: |
| 58 | case USBPHY_TX_SET: |
| 59 | case USBPHY_RX_SET: |
| 60 | case USBPHY_CTRL_SET: |
| 61 | case USBPHY_DEBUG_SET: |
| 62 | case USBPHY_DEBUG1_SET: |
| 63 | /* |
| 64 | * All REG_NAME_SET register access are in fact targeting the |
| 65 | * REG_NAME register. |
| 66 | */ |
| 67 | value = s->usbphy[index - 1]; |
| 68 | break; |
| 69 | case USBPHY_PWD_CLR: |
| 70 | case USBPHY_TX_CLR: |
| 71 | case USBPHY_RX_CLR: |
| 72 | case USBPHY_CTRL_CLR: |
| 73 | case USBPHY_DEBUG_CLR: |
| 74 | case USBPHY_DEBUG1_CLR: |
| 75 | /* |
| 76 | * All REG_NAME_CLR register access are in fact targeting the |
| 77 | * REG_NAME register. |
| 78 | */ |
| 79 | value = s->usbphy[index - 2]; |
| 80 | break; |
| 81 | case USBPHY_PWD_TOG: |
| 82 | case USBPHY_TX_TOG: |
| 83 | case USBPHY_RX_TOG: |
| 84 | case USBPHY_CTRL_TOG: |
| 85 | case USBPHY_DEBUG_TOG: |
| 86 | case USBPHY_DEBUG1_TOG: |
| 87 | /* |
| 88 | * All REG_NAME_TOG register access are in fact targeting the |
| 89 | * REG_NAME register. |
| 90 | */ |
| 91 | value = s->usbphy[index - 3]; |
| 92 | break; |
| 93 | default: |
| 94 | if (index < USBPHY_MAX) { |
| 95 | value = s->usbphy[index]; |
| 96 | } else { |
| 97 | qemu_log_mask(LOG_GUEST_ERROR, |
| 98 | "%s: Read from non-existing USB PHY register 0x%" |
| 99 | HWADDR_PRIx "\n", |
| 100 | __func__, offset); |
| 101 | value = 0; |
| 102 | } |
| 103 | break; |
| 104 | } |
| 105 | return (uint64_t)value; |
| 106 | } |
| 107 | |
| 108 | static void imx_usbphy_write(void *opaque, hwaddr offset, uint64_t value, |
| 109 | unsigned size) |
| 110 | { |
| 111 | IMXUSBPHYState *s = (IMXUSBPHYState *)opaque; |
| 112 | uint32_t index = offset >> 2; |
| 113 | |
| 114 | switch (index) { |
| 115 | case USBPHY_CTRL: |
| 116 | s->usbphy[index] = value; |
| 117 | if (value & USBPHY_CTRL_SFTRST) { |
| 118 | imx_usbphy_softreset(s); |
| 119 | } |
| 120 | break; |
| 121 | case USBPHY_PWD: |
| 122 | case USBPHY_TX: |
| 123 | case USBPHY_RX: |
| 124 | case USBPHY_STATUS: |
| 125 | case USBPHY_DEBUG: |
| 126 | case USBPHY_DEBUG1: |
| 127 | s->usbphy[index] = value; |
| 128 | break; |
| 129 | case USBPHY_CTRL_SET: |
| 130 | s->usbphy[index - 1] |= value; |
| 131 | if (value & USBPHY_CTRL_SFTRST) { |
| 132 | imx_usbphy_softreset(s); |
| 133 | } |
| 134 | break; |
| 135 | case USBPHY_PWD_SET: |
| 136 | case USBPHY_TX_SET: |
| 137 | case USBPHY_RX_SET: |
| 138 | case USBPHY_DEBUG_SET: |
| 139 | case USBPHY_DEBUG1_SET: |
| 140 | /* |
| 141 | * All REG_NAME_SET register access are in fact targeting the |
| 142 | * REG_NAME register. So we change the value of the REG_NAME |
| 143 | * register, setting bits passed in the value. |
| 144 | */ |
| 145 | s->usbphy[index - 1] |= value; |
| 146 | break; |
| 147 | case USBPHY_PWD_CLR: |
| 148 | case USBPHY_TX_CLR: |
| 149 | case USBPHY_RX_CLR: |
| 150 | case USBPHY_CTRL_CLR: |
| 151 | case USBPHY_DEBUG_CLR: |
| 152 | case USBPHY_DEBUG1_CLR: |
| 153 | /* |
| 154 | * All REG_NAME_CLR register access are in fact targeting the |
| 155 | * REG_NAME register. So we change the value of the REG_NAME |
| 156 | * register, unsetting bits passed in the value. |
| 157 | */ |
| 158 | s->usbphy[index - 2] &= ~value; |
| 159 | break; |
| 160 | case USBPHY_CTRL_TOG: |
| 161 | s->usbphy[index - 3] ^= value; |
| 162 | if ((value & USBPHY_CTRL_SFTRST) && |
| 163 | (s->usbphy[index - 3] & USBPHY_CTRL_SFTRST)) { |
| 164 | imx_usbphy_softreset(s); |
| 165 | } |
| 166 | break; |
| 167 | case USBPHY_PWD_TOG: |
| 168 | case USBPHY_TX_TOG: |
| 169 | case USBPHY_RX_TOG: |
| 170 | case USBPHY_DEBUG_TOG: |
| 171 | case USBPHY_DEBUG1_TOG: |
| 172 | /* |
| 173 | * All REG_NAME_TOG register access are in fact targeting the |
| 174 | * REG_NAME register. So we change the value of the REG_NAME |
| 175 | * register, toggling bits passed in the value. |
| 176 | */ |
| 177 | s->usbphy[index - 3] ^= value; |
| 178 | break; |
| 179 | default: |
| 180 | /* Other registers are read-only or do not exist */ |
| 181 | qemu_log_mask(LOG_GUEST_ERROR, |
| 182 | "%s: Write to %s USB PHY register 0x%" |
| 183 | HWADDR_PRIx "\n", |
| 184 | __func__, |
| 185 | index >= USBPHY_MAX ? "non-existing" : "read-only", |
| 186 | offset); |
| 187 | break; |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | static const struct MemoryRegionOps imx_usbphy_ops = { |
| 192 | .read = imx_usbphy_read, |
| 193 | .write = imx_usbphy_write, |
| 194 | .endianness = DEVICE_NATIVE_ENDIAN, |
| 195 | .valid = { |
| 196 | /* |
| 197 | * Our device would not work correctly if the guest was doing |
| 198 | * unaligned access. This might not be a limitation on the real |
| 199 | * device but in practice there is no reason for a guest to access |
| 200 | * this device unaligned. |
| 201 | */ |
| 202 | .min_access_size = 4, |
| 203 | .max_access_size = 4, |
| 204 | .unaligned = false, |
| 205 | }, |
| 206 | }; |
| 207 | |
| 208 | static void imx_usbphy_realize(DeviceState *dev, Error **errp) |
| 209 | { |
| 210 | IMXUSBPHYState *s = IMX_USBPHY(dev); |
| 211 | |
| 212 | memory_region_init_io(&s->iomem, OBJECT(s), &imx_usbphy_ops, s, |
| 213 | "imx-usbphy", 0x1000); |
| 214 | sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); |
| 215 | } |
| 216 | |
| 217 | static void imx_usbphy_class_init(ObjectClass *klass, const void *data) |
| 218 | { |
| 219 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 220 | |
| 221 | device_class_set_legacy_reset(dc, imx_usbphy_reset); |
| 222 | dc->vmsd = &vmstate_imx_usbphy; |
| 223 | dc->desc = "i.MX USB PHY Module"; |
| 224 | dc->realize = imx_usbphy_realize; |
| 225 | } |
| 226 | |
| 227 | static const TypeInfo imx_usbphy_info = { |
| 228 | .name = TYPE_IMX_USBPHY, |
| 229 | .parent = TYPE_SYS_BUS_DEVICE, |
| 230 | .instance_size = sizeof(IMXUSBPHYState), |
| 231 | .class_init = imx_usbphy_class_init, |
| 232 | }; |
| 233 | |
| 234 | static void imx_usbphy_register_types(void) |
| 235 | { |
| 236 | type_register_static(&imx_usbphy_info); |
| 237 | } |
| 238 | |
| 239 | type_init(imx_usbphy_register_types) |