📄 usbdcore_mpc8xx.c
字号:
/* * Copyright (C) 2006 by Bryan O'Donoghue, CodeHermit * bodonoghue@CodeHermit.ie * * References * DasUBoot/drivers/usb/usbdcore_omap1510.c, for design and implementation * ideas. * * 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 of the License, or * (at your option) any later version. * * 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. * *//* * Notes : * 1. #define __SIMULATE_ERROR__ to inject a CRC error into every 2nd TX * packet to force the USB re-transmit protocol. * * 2. #define __DEBUG_UDC__ to switch on debug tracing to serial console * be careful that tracing doesn't create Hiesen-bugs with respect to * response timeouts to control requests. * * 3. This driver should be able to support any higher level driver that * that wants to do either of the two standard UDC implementations * Control-Bulk-Interrupt or Bulk-IN/Bulk-Out standards. Hence * gserial and cdc_acm should work with this code. * * 4. NAK events never actually get raised at all, the documentation * is just wrong ! * * 5. For some reason, cbd_datlen is *always* +2 the value it should be. * this means that having an RX cbd of 16 bytes is not possible, since * the same size is reported for 14 bytes received as 16 bytes received * until we can find out why this happens, RX cbds must be limited to 8 * bytes. TODO: check errata for this behaviour. * * 6. Right now this code doesn't support properly powering up with the USB * cable attached to the USB host my development board the Adder87x doesn't * have a pull-up fitted to allow this, so it is necessary to power the * board and *then* attached the USB cable to the host. However somebody * with a different design in their board may be able to keep the cable * constantly connected and simply enable/disable a pull-up re * figure 31.1 in MPC885RM.pdf instead of having to power up the board and * then attach the cable ! * */#include <common.h>#include <config.h>#if defined(CONFIG_MPC885_FAMILY) && defined(CONFIG_USB_DEVICE)#include <commproc.h>#include "usbdcore.h"#include "usbdcore_mpc8xx.h"#include "usbdcore_ep0.h"DECLARE_GLOBAL_DATA_PTR;#define ERR(fmt, args...)\ serial_printf("ERROR : [%s] %s:%d: "fmt,\ __FILE__,__FUNCTION__,__LINE__, ##args)#ifdef __DEBUG_UDC__#define DBG(fmt,args...)\ serial_printf("[%s] %s:%d: "fmt,\ __FILE__,__FUNCTION__,__LINE__, ##args)#else#define DBG(fmt,args...)#endif/* Static Data */#ifdef __SIMULATE_ERROR__static char err_poison_test = 0;#endifstatic struct mpc8xx_ep ep_ref[MAX_ENDPOINTS];static u32 address_base = STATE_NOT_READY;static mpc8xx_udc_state_t udc_state = 0;static struct usb_device_instance *udc_device = 0;static volatile usb_epb_t *endpoints[MAX_ENDPOINTS];static volatile cbd_t *tx_cbd[TX_RING_SIZE];static volatile cbd_t *rx_cbd[RX_RING_SIZE];static volatile immap_t *immr = 0;static volatile cpm8xx_t *cp = 0;static volatile usb_pram_t *usb_paramp = 0;static volatile usb_t *usbp = 0;static int rx_ct = 0;static int tx_ct = 0;/* Static Function Declarations */static void mpc8xx_udc_state_transition_up (usb_device_state_t initial, usb_device_state_t final);static void mpc8xx_udc_state_transition_down (usb_device_state_t initial, usb_device_state_t final);static void mpc8xx_udc_stall (unsigned int ep);static void mpc8xx_udc_flush_tx_fifo (int epid);static void mpc8xx_udc_flush_rx_fifo (void);static void mpc8xx_udc_clear_rxbd (volatile cbd_t * rx_cbdp);static void mpc8xx_udc_init_tx (struct usb_endpoint_instance *epi, struct urb *tx_urb);static void mpc8xx_udc_dump_request (struct usb_device_request *request);static void mpc8xx_udc_clock_init (volatile immap_t * immr, volatile cpm8xx_t * cp);static int mpc8xx_udc_ep_tx (struct usb_endpoint_instance *epi);static int mpc8xx_udc_epn_rx (unsigned int epid, volatile cbd_t * rx_cbdp);static void mpc8xx_udc_ep0_rx (volatile cbd_t * rx_cbdp);static void mpc8xx_udc_cbd_init (void);static void mpc8xx_udc_endpoint_init (void);static void mpc8xx_udc_cbd_attach (int ep, uchar tx_size, uchar rx_size);static u32 mpc8xx_udc_alloc (u32 data_size, u32 alignment);static int mpc8xx_udc_ep0_rx_setup (volatile cbd_t * rx_cbdp);static void mpc8xx_udc_set_nak (unsigned int ep);static short mpc8xx_udc_handle_txerr (void);static void mpc8xx_udc_advance_rx (volatile cbd_t ** rx_cbdp, int epid);/****************************************************************************** Global Linkage *****************************************************************************//* udc_init * * Do initial bus gluing */int udc_init (void){ /* Init various pointers */ immr = (immap_t *) CFG_IMMR; cp = (cpm8xx_t *) & (immr->im_cpm); usb_paramp = (usb_pram_t *) & (cp->cp_dparam[PROFF_USB]); usbp = (usb_t *) & (cp->cp_scc[0]); memset (ep_ref, 0x00, (sizeof (struct mpc8xx_ep) * MAX_ENDPOINTS)); udc_device = 0; udc_state = STATE_NOT_READY; usbp->usmod = 0x00; usbp->uscom = 0; /* Set USB Frame #0, Respond at Address & Get a clock source */ usbp->usaddr = 0x00; mpc8xx_udc_clock_init (immr, cp); /* PA15, PA14 as perhiperal USBRXD and USBOE */ immr->im_ioport.iop_padir &= ~0x0003; immr->im_ioport.iop_papar |= 0x0003; /* PC11/PC10 as peripheral USBRXP USBRXN */ immr->im_ioport.iop_pcso |= 0x0030; /* PC7/PC6 as perhiperal USBTXP and USBTXN */ immr->im_ioport.iop_pcdir |= 0x0300; immr->im_ioport.iop_pcpar |= 0x0300; /* Set the base address */ address_base = (u32) (cp->cp_dpmem + CPM_USB_BASE); /* Initialise endpoints and circular buffers */ mpc8xx_udc_endpoint_init (); mpc8xx_udc_cbd_init (); /* Assign allocated Dual Port Endpoint descriptors */ usb_paramp->ep0ptr = (u32) endpoints[0]; usb_paramp->ep1ptr = (u32) endpoints[1]; usb_paramp->ep2ptr = (u32) endpoints[2]; usb_paramp->ep3ptr = (u32) endpoints[3]; usb_paramp->frame_n = 0; DBG ("ep0ptr=0x%08x ep1ptr=0x%08x ep2ptr=0x%08x ep3ptr=0x%08x\n", usb_paramp->ep0ptr, usb_paramp->ep1ptr, usb_paramp->ep2ptr, usb_paramp->ep3ptr); return 0;}/* udc_irq * * Poll for whatever events may have occured */void udc_irq (void){ int epid = 0; volatile cbd_t *rx_cbdp = 0; volatile cbd_t *rx_cbdp_base = 0; if (udc_state != STATE_READY) { return; } if (usbp->usber & USB_E_BSY) { /* This shouldn't happen. If it does then it's a bug ! */ usbp->usber |= USB_E_BSY; mpc8xx_udc_flush_rx_fifo (); } /* Scan all RX/Bidirectional Endpoints for RX data. */ for (epid = 0; epid < MAX_ENDPOINTS; epid++) { if (!ep_ref[epid].prx) { continue; } rx_cbdp = rx_cbdp_base = ep_ref[epid].prx; do { if (!(rx_cbdp->cbd_sc & RX_BD_E)) { if (rx_cbdp->cbd_sc & 0x1F) { /* Corrupt data discard it. * Controller has NAK'd this packet. */ mpc8xx_udc_clear_rxbd (rx_cbdp); } else { if (!epid) { mpc8xx_udc_ep0_rx (rx_cbdp); } else { /* Process data */ mpc8xx_udc_set_nak (epid); mpc8xx_udc_epn_rx (epid, rx_cbdp); mpc8xx_udc_clear_rxbd (rx_cbdp); } } /* Advance RX CBD pointer */ mpc8xx_udc_advance_rx (&rx_cbdp, epid); ep_ref[epid].prx = rx_cbdp; } else { /* Advance RX CBD pointer */ mpc8xx_udc_advance_rx (&rx_cbdp, epid); } } while (rx_cbdp != rx_cbdp_base); } /* Handle TX events as appropiate, the correct place to do this is * in a tx routine. Perhaps TX on epn was pre-empted by ep0 */ if (usbp->usber & USB_E_TXB) { usbp->usber |= USB_E_TXB; } if (usbp->usber & (USB_TX_ERRMASK)) { mpc8xx_udc_handle_txerr (); } /* Switch to the default state, respond at the default address */ if (usbp->usber & USB_E_RESET) { usbp->usber |= USB_E_RESET; usbp->usaddr = 0x00; udc_device->device_state = STATE_DEFAULT; } /* if(usbp->usber&USB_E_IDLE){ We could suspend here ! usbp->usber|=USB_E_IDLE; DBG("idle state change\n"); } if(usbp->usbs){ We could resume here when IDLE is deasserted ! Not worth doing, so long as we are self powered though. } */ return;}/* udc_endpoint_write * * Write some data to an endpoint */int udc_endpoint_write (struct usb_endpoint_instance *epi){ int ep = 0; short epid = 1, unnak = 0, ret = 0; if (udc_state != STATE_READY) { ERR ("invalid udc_state != STATE_READY!\n"); return -1; } if (!udc_device || !epi) { return -1; } if (udc_device->device_state != STATE_CONFIGURED) { return -1; } ep = epi->endpoint_address & 0x03; if (ep >= MAX_ENDPOINTS) { return -1; } /* Set NAK for all RX endpoints during TX */ for (epid = 1; epid < MAX_ENDPOINTS; epid++) { /* Don't set NAK on DATA IN/CONTROL endpoints */ if (ep_ref[epid].sc & USB_DIR_IN) { continue; } if (!(usbp->usep[epid] & (USEP_THS_NAK | USEP_RHS_NAK))) { unnak |= 1 << epid; } mpc8xx_udc_set_nak (epid); } mpc8xx_udc_init_tx (&udc_device->bus->endpoint_array[ep], epi->tx_urb); ret = mpc8xx_udc_ep_tx (&udc_device->bus->endpoint_array[ep]); /* Remove temporary NAK */ for (epid = 1; epid < MAX_ENDPOINTS; epid++) { if (unnak & (1 << epid)) { udc_unset_nak (epid); } } return ret;}/* mpc8xx_udc_assign_urb * * Associate a given urb to an endpoint TX or RX transmit/receive buffers */static int mpc8xx_udc_assign_urb (int ep, char direction){ struct usb_endpoint_instance *epi = 0; if (ep >= MAX_ENDPOINTS) { goto err; } epi = &udc_device->bus->endpoint_array[ep]; if (!epi) { goto err; } if (!ep_ref[ep].urb) { ep_ref[ep].urb = usbd_alloc_urb (udc_device, udc_device->bus->endpoint_array); if (!ep_ref[ep].urb) { goto err; } } else { ep_ref[ep].urb->actual_length = 0; } switch (direction) { case USB_DIR_IN: epi->tx_urb = ep_ref[ep].urb; break; case USB_DIR_OUT: epi->rcv_urb = ep_ref[ep].urb; break; default: goto err; } return 0; err: udc_state = STATE_ERROR; return -1;}/* udc_setup_ep * * Associate U-Boot software endpoints to mpc8xx endpoint parameter ram * Isochronous endpoints aren't yet supported! */void udc_setup_ep (struct usb_device_instance *device, unsigned int ep, struct usb_endpoint_instance *epi){ uchar direction = 0; int ep_attrib = 0; if (epi && (ep < MAX_ENDPOINTS)) { if (ep == 0) { if (epi->rcv_attributes != USB_ENDPOINT_XFER_CONTROL || epi->tx_attributes != USB_ENDPOINT_XFER_CONTROL) { /* ep0 must be a control endpoint */ udc_state = STATE_ERROR; return; } if (!(ep_ref[ep].sc & EP_ATTACHED)) { mpc8xx_udc_cbd_attach (ep, epi->tx_packetSize, epi->rcv_packetSize); } usbp->usep[ep] = 0x0000; return; } if ((epi->endpoint_address & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) { direction = 1; ep_attrib = epi->tx_attributes; epi->rcv_packetSize = 0; ep_ref[ep].sc |= USB_DIR_IN; } else { direction = 0; ep_attrib = epi->rcv_attributes; epi->tx_packetSize = 0; ep_ref[ep].sc &= ~USB_DIR_IN; } if (mpc8xx_udc_assign_urb (ep, epi->endpoint_address & USB_ENDPOINT_DIR_MASK)) { return; } switch (ep_attrib) { case USB_ENDPOINT_XFER_CONTROL: if (!(ep_ref[ep].sc & EP_ATTACHED)) { mpc8xx_udc_cbd_attach (ep, epi->tx_packetSize, epi->rcv_packetSize); } usbp->usep[ep] = ep << 12; epi->rcv_urb = epi->tx_urb = ep_ref[ep].urb; break; case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: if (!(ep_ref[ep].sc & EP_ATTACHED)) { if (direction) { mpc8xx_udc_cbd_attach (ep, epi->tx_packetSize, 0); } else { mpc8xx_udc_cbd_attach (ep, 0, epi->rcv_packetSize); } } usbp->usep[ep] = (ep << 12) | ((ep_attrib) << 8); break; case USB_ENDPOINT_XFER_ISOC: default: serial_printf ("Error endpoint attrib %d>3\n", ep_attrib); udc_state = STATE_ERROR; break; } }}/* udc_connect * * Move state, switch on the USB */void udc_connect (void){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -