 0701a5efa0
			
		
	
	
		0701a5efa0
		
	
	
	
	
		
			
			Add basic USB PHY support as implemented in i.MX23, i.MX28, i.MX6, and i.MX7 SoCs. The only support really needed - at least to boot Linux - is support for soft reset, which needs to reset various registers to their initial value. Otherwise, just record register values. Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Message-id: 20200313014551.12554-2-linux@roeck-us.net Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
		
			
				
	
	
		
			226 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			226 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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/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)
 |