📄 usb-musb.c
字号:
/* * "Inventra" High-speed Dual-Role Controller (MUSB-HDRC), Mentor Graphics, * USB2.0 OTG compliant core used in various chips. * * Copyright (C) 2008 Nokia Corporation * Written by Andrzej Zaborowski <andrew@openedhand.com> * * 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 or * (at your option) version 3 of the License. * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * Only host-mode and non-DMA accesses are currently supported. */#include "qemu-common.h"#include "qemu-timer.h"#include "usb.h"#include "irq.h"/* Common USB registers */#define MUSB_HDRC_FADDR 0x00 /* 8-bit */#define MUSB_HDRC_POWER 0x01 /* 8-bit */#define MUSB_HDRC_INTRTX 0x02 /* 16-bit */#define MUSB_HDRC_INTRRX 0x04#define MUSB_HDRC_INTRTXE 0x06 #define MUSB_HDRC_INTRRXE 0x08 #define MUSB_HDRC_INTRUSB 0x0a /* 8 bit */#define MUSB_HDRC_INTRUSBE 0x0b /* 8 bit */#define MUSB_HDRC_FRAME 0x0c /* 16-bit */#define MUSB_HDRC_INDEX 0x0e /* 8 bit */#define MUSB_HDRC_TESTMODE 0x0f /* 8 bit *//* Per-EP registers in indexed mode */#define MUSB_HDRC_EP_IDX 0x10 /* 8-bit *//* EP FIFOs */#define MUSB_HDRC_FIFO 0x20/* Additional Control Registers */#define MUSB_HDRC_DEVCTL 0x60 /* 8 bit *//* These are indexed */#define MUSB_HDRC_TXFIFOSZ 0x62 /* 8 bit (see masks) */#define MUSB_HDRC_RXFIFOSZ 0x63 /* 8 bit (see masks) */#define MUSB_HDRC_TXFIFOADDR 0x64 /* 16 bit offset shifted right 3 */#define MUSB_HDRC_RXFIFOADDR 0x66 /* 16 bit offset shifted right 3 *//* Some more registers */#define MUSB_HDRC_VCTRL 0x68 /* 8 bit */#define MUSB_HDRC_HWVERS 0x6c /* 8 bit *//* Added in HDRC 1.9(?) & MHDRC 1.4 *//* ULPI pass-through */#define MUSB_HDRC_ULPI_VBUSCTL 0x70#define MUSB_HDRC_ULPI_REGDATA 0x74#define MUSB_HDRC_ULPI_REGADDR 0x75#define MUSB_HDRC_ULPI_REGCTL 0x76/* Extended config & PHY control */#define MUSB_HDRC_ENDCOUNT 0x78 /* 8 bit */#define MUSB_HDRC_DMARAMCFG 0x79 /* 8 bit */#define MUSB_HDRC_PHYWAIT 0x7a /* 8 bit */#define MUSB_HDRC_PHYVPLEN 0x7b /* 8 bit */#define MUSB_HDRC_HS_EOF1 0x7c /* 8 bit, units of 546.1 us */#define MUSB_HDRC_FS_EOF1 0x7d /* 8 bit, units of 533.3 ns */#define MUSB_HDRC_LS_EOF1 0x7e /* 8 bit, units of 1.067 us *//* Per-EP BUSCTL registers */#define MUSB_HDRC_BUSCTL 0x80/* Per-EP registers in flat mode */#define MUSB_HDRC_EP 0x100/* offsets to registers in flat model */#define MUSB_HDRC_TXMAXP 0x00 /* 16 bit apparently */#define MUSB_HDRC_TXCSR 0x02 /* 16 bit apparently */#define MUSB_HDRC_CSR0 MUSB_HDRC_TXCSR /* re-used for EP0 */#define MUSB_HDRC_RXMAXP 0x04 /* 16 bit apparently */#define MUSB_HDRC_RXCSR 0x06 /* 16 bit apparently */#define MUSB_HDRC_RXCOUNT 0x08 /* 16 bit apparently */#define MUSB_HDRC_COUNT0 MUSB_HDRC_RXCOUNT /* re-used for EP0 */#define MUSB_HDRC_TXTYPE 0x0a /* 8 bit apparently */#define MUSB_HDRC_TYPE0 MUSB_HDRC_TXTYPE /* re-used for EP0 */#define MUSB_HDRC_TXINTERVAL 0x0b /* 8 bit apparently */#define MUSB_HDRC_NAKLIMIT0 MUSB_HDRC_TXINTERVAL /* re-used for EP0 */#define MUSB_HDRC_RXTYPE 0x0c /* 8 bit apparently */#define MUSB_HDRC_RXINTERVAL 0x0d /* 8 bit apparently */#define MUSB_HDRC_FIFOSIZE 0x0f /* 8 bit apparently */#define MUSB_HDRC_CONFIGDATA MGC_O_HDRC_FIFOSIZE /* re-used for EP0 *//* "Bus control" registers */#define MUSB_HDRC_TXFUNCADDR 0x00#define MUSB_HDRC_TXHUBADDR 0x02#define MUSB_HDRC_TXHUBPORT 0x03#define MUSB_HDRC_RXFUNCADDR 0x04#define MUSB_HDRC_RXHUBADDR 0x06#define MUSB_HDRC_RXHUBPORT 0x07/* * MUSBHDRC Register bit masks *//* POWER */#define MGC_M_POWER_ISOUPDATE 0x80 #define MGC_M_POWER_SOFTCONN 0x40#define MGC_M_POWER_HSENAB 0x20#define MGC_M_POWER_HSMODE 0x10#define MGC_M_POWER_RESET 0x08#define MGC_M_POWER_RESUME 0x04#define MGC_M_POWER_SUSPENDM 0x02#define MGC_M_POWER_ENSUSPEND 0x01/* INTRUSB */#define MGC_M_INTR_SUSPEND 0x01#define MGC_M_INTR_RESUME 0x02#define MGC_M_INTR_RESET 0x04#define MGC_M_INTR_BABBLE 0x04#define MGC_M_INTR_SOF 0x08 #define MGC_M_INTR_CONNECT 0x10#define MGC_M_INTR_DISCONNECT 0x20#define MGC_M_INTR_SESSREQ 0x40#define MGC_M_INTR_VBUSERROR 0x80 /* FOR SESSION END */#define MGC_M_INTR_EP0 0x01 /* FOR EP0 INTERRUPT *//* DEVCTL */#define MGC_M_DEVCTL_BDEVICE 0x80 #define MGC_M_DEVCTL_FSDEV 0x40#define MGC_M_DEVCTL_LSDEV 0x20#define MGC_M_DEVCTL_VBUS 0x18#define MGC_S_DEVCTL_VBUS 3#define MGC_M_DEVCTL_HM 0x04#define MGC_M_DEVCTL_HR 0x02#define MGC_M_DEVCTL_SESSION 0x01/* TESTMODE */#define MGC_M_TEST_FORCE_HOST 0x80#define MGC_M_TEST_FIFO_ACCESS 0x40#define MGC_M_TEST_FORCE_FS 0x20#define MGC_M_TEST_FORCE_HS 0x10#define MGC_M_TEST_PACKET 0x08#define MGC_M_TEST_K 0x04#define MGC_M_TEST_J 0x02#define MGC_M_TEST_SE0_NAK 0x01/* CSR0 */#define MGC_M_CSR0_FLUSHFIFO 0x0100#define MGC_M_CSR0_TXPKTRDY 0x0002#define MGC_M_CSR0_RXPKTRDY 0x0001/* CSR0 in Peripheral mode */#define MGC_M_CSR0_P_SVDSETUPEND 0x0080#define MGC_M_CSR0_P_SVDRXPKTRDY 0x0040#define MGC_M_CSR0_P_SENDSTALL 0x0020#define MGC_M_CSR0_P_SETUPEND 0x0010#define MGC_M_CSR0_P_DATAEND 0x0008#define MGC_M_CSR0_P_SENTSTALL 0x0004/* CSR0 in Host mode */#define MGC_M_CSR0_H_NO_PING 0x0800#define MGC_M_CSR0_H_WR_DATATOGGLE 0x0400 /* set to allow setting: */#define MGC_M_CSR0_H_DATATOGGLE 0x0200 /* data toggle control */#define MGC_M_CSR0_H_NAKTIMEOUT 0x0080#define MGC_M_CSR0_H_STATUSPKT 0x0040#define MGC_M_CSR0_H_REQPKT 0x0020#define MGC_M_CSR0_H_ERROR 0x0010#define MGC_M_CSR0_H_SETUPPKT 0x0008#define MGC_M_CSR0_H_RXSTALL 0x0004/* CONFIGDATA */#define MGC_M_CONFIGDATA_MPRXE 0x80 /* auto bulk pkt combining */#define MGC_M_CONFIGDATA_MPTXE 0x40 /* auto bulk pkt splitting */#define MGC_M_CONFIGDATA_BIGENDIAN 0x20#define MGC_M_CONFIGDATA_HBRXE 0x10 /* HB-ISO for RX */#define MGC_M_CONFIGDATA_HBTXE 0x08 /* HB-ISO for TX */#define MGC_M_CONFIGDATA_DYNFIFO 0x04 /* dynamic FIFO sizing */#define MGC_M_CONFIGDATA_SOFTCONE 0x02 /* SoftConnect */#define MGC_M_CONFIGDATA_UTMIDW 0x01 /* Width, 0 => 8b, 1 => 16b *//* TXCSR in Peripheral and Host mode */#define MGC_M_TXCSR_AUTOSET 0x8000#define MGC_M_TXCSR_ISO 0x4000#define MGC_M_TXCSR_MODE 0x2000#define MGC_M_TXCSR_DMAENAB 0x1000#define MGC_M_TXCSR_FRCDATATOG 0x0800#define MGC_M_TXCSR_DMAMODE 0x0400#define MGC_M_TXCSR_CLRDATATOG 0x0040#define MGC_M_TXCSR_FLUSHFIFO 0x0008#define MGC_M_TXCSR_FIFONOTEMPTY 0x0002#define MGC_M_TXCSR_TXPKTRDY 0x0001/* TXCSR in Peripheral mode */#define MGC_M_TXCSR_P_INCOMPTX 0x0080#define MGC_M_TXCSR_P_SENTSTALL 0x0020#define MGC_M_TXCSR_P_SENDSTALL 0x0010#define MGC_M_TXCSR_P_UNDERRUN 0x0004/* TXCSR in Host mode */#define MGC_M_TXCSR_H_WR_DATATOGGLE 0x0200#define MGC_M_TXCSR_H_DATATOGGLE 0x0100#define MGC_M_TXCSR_H_NAKTIMEOUT 0x0080#define MGC_M_TXCSR_H_RXSTALL 0x0020#define MGC_M_TXCSR_H_ERROR 0x0004/* RXCSR in Peripheral and Host mode */#define MGC_M_RXCSR_AUTOCLEAR 0x8000#define MGC_M_RXCSR_DMAENAB 0x2000#define MGC_M_RXCSR_DISNYET 0x1000#define MGC_M_RXCSR_DMAMODE 0x0800#define MGC_M_RXCSR_INCOMPRX 0x0100#define MGC_M_RXCSR_CLRDATATOG 0x0080#define MGC_M_RXCSR_FLUSHFIFO 0x0010#define MGC_M_RXCSR_DATAERROR 0x0008#define MGC_M_RXCSR_FIFOFULL 0x0002#define MGC_M_RXCSR_RXPKTRDY 0x0001/* RXCSR in Peripheral mode */#define MGC_M_RXCSR_P_ISO 0x4000#define MGC_M_RXCSR_P_SENTSTALL 0x0040#define MGC_M_RXCSR_P_SENDSTALL 0x0020#define MGC_M_RXCSR_P_OVERRUN 0x0004/* RXCSR in Host mode */#define MGC_M_RXCSR_H_AUTOREQ 0x4000#define MGC_M_RXCSR_H_WR_DATATOGGLE 0x0400#define MGC_M_RXCSR_H_DATATOGGLE 0x0200#define MGC_M_RXCSR_H_RXSTALL 0x0040#define MGC_M_RXCSR_H_REQPKT 0x0020#define MGC_M_RXCSR_H_ERROR 0x0004/* HUBADDR */#define MGC_M_HUBADDR_MULTI_TT 0x80/* ULPI: Added in HDRC 1.9(?) & MHDRC 1.4 */#define MGC_M_ULPI_VBCTL_USEEXTVBUSIND 0x02#define MGC_M_ULPI_VBCTL_USEEXTVBUS 0x01#define MGC_M_ULPI_REGCTL_INT_ENABLE 0x08#define MGC_M_ULPI_REGCTL_READNOTWRITE 0x04#define MGC_M_ULPI_REGCTL_COMPLETE 0x02#define MGC_M_ULPI_REGCTL_REG 0x01static void musb_attach(USBPort *port, USBDevice *dev);struct musb_s { qemu_irq *irqs; USBPort port; int idx; uint8_t devctl; uint8_t power; uint8_t faddr; uint8_t intr; uint8_t mask; uint16_t tx_intr; uint16_t tx_mask; uint16_t rx_intr; uint16_t rx_mask; int setup_len; int session; uint32_t buf[0x2000]; struct musb_ep_s { uint16_t faddr[2]; uint8_t haddr[2]; uint8_t hport[2]; uint16_t csr[2]; uint16_t maxp[2]; uint16_t rxcount; uint8_t type[2]; uint8_t interval[2]; uint8_t config; uint8_t fifosize; int timeout[2]; /* Always in microframes */ uint32_t *buf[2]; int fifolen[2]; int fifostart[2]; int fifoaddr[2]; USBPacket packey[2]; int status[2]; int ext_size[2]; /* For callbacks' use */ int epnum; int interrupt[2]; struct musb_s *musb; USBCallback *delayed_cb[2]; QEMUTimer *intv_timer[2]; /* Duplicating the world since 2008!... probably we should have 32 * logical, single endpoints instead. */ } ep[16];} *musb_init(qemu_irq *irqs){ struct musb_s *s = qemu_mallocz(sizeof(*s)); int i; s->irqs = irqs; s->faddr = 0x00; s->power = MGC_M_POWER_HSENAB; s->tx_intr = 0x0000; s->rx_intr = 0x0000; s->tx_mask = 0xffff; s->rx_mask = 0xffff; s->intr = 0x00; s->mask = 0x06; s->idx = 0; /* TODO: _DW */ s->ep[0].config = MGC_M_CONFIGDATA_SOFTCONE | MGC_M_CONFIGDATA_DYNFIFO; for (i = 0; i < 16; i ++) { s->ep[i].fifosize = 64; s->ep[i].maxp[0] = 0x40; s->ep[i].maxp[1] = 0x40; s->ep[i].musb = s; s->ep[i].epnum = i; } qemu_register_usb_port(&s->port, s, 0, musb_attach); return s;}static void musb_vbus_set(struct musb_s *s, int level){ if (level) s->devctl |= 3 << MGC_S_DEVCTL_VBUS; else s->devctl &= ~MGC_M_DEVCTL_VBUS; qemu_set_irq(s->irqs[musb_set_vbus], level);}static void musb_intr_set(struct musb_s *s, int line, int level){ if (!level) { s->intr &= ~(1 << line); qemu_irq_lower(s->irqs[line]); } else if (s->mask & (1 << line)) { s->intr |= 1 << line; qemu_irq_raise(s->irqs[line]); }}static void musb_tx_intr_set(struct musb_s *s, int line, int level){ if (!level) { s->tx_intr &= ~(1 << line); if (!s->tx_intr) qemu_irq_lower(s->irqs[musb_irq_tx]); } else if (s->tx_mask & (1 << line)) { s->tx_intr |= 1 << line; qemu_irq_raise(s->irqs[musb_irq_tx]); }}static void musb_rx_intr_set(struct musb_s *s, int line, int level){ if (line) { if (!level) { s->rx_intr &= ~(1 << line); if (!s->rx_intr) qemu_irq_lower(s->irqs[musb_irq_rx]); } else if (s->rx_mask & (1 << line)) { s->rx_intr |= 1 << line; qemu_irq_raise(s->irqs[musb_irq_rx]); } } else musb_tx_intr_set(s, line, level);}uint32_t musb_core_intr_get(struct musb_s *s){ return (s->rx_intr << 15) | s->tx_intr;}void musb_core_intr_clear(struct musb_s *s, uint32_t mask){ if (s->rx_intr) { s->rx_intr &= mask >> 15; if (!s->rx_intr) qemu_irq_lower(s->irqs[musb_irq_rx]); } if (s->tx_intr) { s->tx_intr &= mask & 0xffff; if (!s->tx_intr) qemu_irq_lower(s->irqs[musb_irq_tx]); }}void musb_set_size(struct musb_s *s, int epnum, int size, int is_tx){ s->ep[epnum].ext_size[!is_tx] = size; s->ep[epnum].fifostart[0] = 0; s->ep[epnum].fifostart[1] = 0; s->ep[epnum].fifolen[0] = 0; s->ep[epnum].fifolen[1] = 0;}static void musb_session_update(struct musb_s *s, int prev_dev, int prev_sess){ int detect_prev = prev_dev && prev_sess; int detect = !!s->port.dev && s->session; if (detect && !detect_prev) { /* Let's skip the ID pin sense and VBUS sense formalities and * and signal a successful SRP directly. This should work at least * for the Linux driver stack. */ musb_intr_set(s, musb_irq_connect, 1); if (s->port.dev->speed == USB_SPEED_LOW) { s->devctl &= ~MGC_M_DEVCTL_FSDEV; s->devctl |= MGC_M_DEVCTL_LSDEV; } else { s->devctl |= MGC_M_DEVCTL_FSDEV; s->devctl &= ~MGC_M_DEVCTL_LSDEV; } /* A-mode? */ s->devctl &= ~MGC_M_DEVCTL_BDEVICE; /* Host-mode bit? */ s->devctl |= MGC_M_DEVCTL_HM;#if 1 musb_vbus_set(s, 1);#endif } else if (!detect && detect_prev) {#if 1 musb_vbus_set(s, 0);#endif }}/* Attach or detach a device on our only port. */static void musb_attach(USBPort *port, USBDevice *dev){ struct musb_s *s = (struct musb_s *) port->opaque; USBDevice *curr; port = &s->port; curr = port->dev; if (dev) { if (curr) { usb_attach(port, NULL); /* TODO: signal some interrupts */ } musb_intr_set(s, musb_irq_vbus_request, 1); /* Send the attach message to device */ usb_send_msg(dev, USB_MSG_ATTACH); } else if (curr) { /* Send the detach message */ usb_send_msg(curr, USB_MSG_DETACH); musb_intr_set(s, musb_irq_disconnect, 1); } port->dev = dev; musb_session_update(s, !!curr, s->session);}static inline void musb_cb_tick0(void *opaque){ struct musb_ep_s *ep = (struct musb_ep_s *) opaque; ep->delayed_cb[0](&ep->packey[0], opaque);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -