123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Freescale QUICC Engine USB Host Controller Driver
- *
- * Copyright (c) Freescale Semicondutor, Inc. 2006.
- * Shlomi Gridish <gridish@freescale.com>
- * Jerry Huang <Chang-Ming.Huang@freescale.com>
- * Copyright (c) Logic Product Development, Inc. 2007
- * Peter Barada <peterb@logicpd.com>
- * Copyright (c) MontaVista Software, Inc. 2008.
- * Anton Vorontsov <avorontsov@ru.mvista.com>
- */
- #include <linux/kernel.h>
- #include <linux/types.h>
- #include <linux/errno.h>
- #include <linux/slab.h>
- #include <linux/list.h>
- #include <linux/io.h>
- #include <linux/usb.h>
- #include <linux/usb/hcd.h>
- #include "fhci.h"
- #define DUMMY_BD_BUFFER 0xdeadbeef
- #define DUMMY2_BD_BUFFER 0xbaadf00d
- /* Transaction Descriptors bits */
- #define TD_R 0x8000 /* ready bit */
- #define TD_W 0x2000 /* wrap bit */
- #define TD_I 0x1000 /* interrupt on completion */
- #define TD_L 0x0800 /* last */
- #define TD_TC 0x0400 /* transmit CRC */
- #define TD_CNF 0x0200 /* CNF - Must be always 1 */
- #define TD_LSP 0x0100 /* Low-speed transaction */
- #define TD_PID 0x00c0 /* packet id */
- #define TD_RXER 0x0020 /* Rx error or not */
- #define TD_NAK 0x0010 /* No ack. */
- #define TD_STAL 0x0008 /* Stall received */
- #define TD_TO 0x0004 /* time out */
- #define TD_UN 0x0002 /* underrun */
- #define TD_NO 0x0010 /* Rx Non Octet Aligned Packet */
- #define TD_AB 0x0008 /* Frame Aborted */
- #define TD_CR 0x0004 /* CRC Error */
- #define TD_OV 0x0002 /* Overrun */
- #define TD_BOV 0x0001 /* Buffer Overrun */
- #define TD_ERRORS (TD_NAK | TD_STAL | TD_TO | TD_UN | \
- TD_NO | TD_AB | TD_CR | TD_OV | TD_BOV)
- #define TD_PID_DATA0 0x0080 /* Data 0 toggle */
- #define TD_PID_DATA1 0x00c0 /* Data 1 toggle */
- #define TD_PID_TOGGLE 0x00c0 /* Data 0/1 toggle mask */
- #define TD_TOK_SETUP 0x0000
- #define TD_TOK_OUT 0x4000
- #define TD_TOK_IN 0x8000
- #define TD_ISO 0x1000
- #define TD_ENDP 0x0780
- #define TD_ADDR 0x007f
- #define TD_ENDP_SHIFT 7
- struct usb_td {
- __be16 status;
- __be16 length;
- __be32 buf_ptr;
- __be16 extra;
- __be16 reserved;
- };
- static struct usb_td __iomem *next_bd(struct usb_td __iomem *base,
- struct usb_td __iomem *td,
- u16 status)
- {
- if (status & TD_W)
- return base;
- else
- return ++td;
- }
- void fhci_push_dummy_bd(struct endpoint *ep)
- {
- if (!ep->already_pushed_dummy_bd) {
- u16 td_status = in_be16(&ep->empty_td->status);
- out_be32(&ep->empty_td->buf_ptr, DUMMY_BD_BUFFER);
- /* get the next TD in the ring */
- ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status);
- ep->already_pushed_dummy_bd = true;
- }
- }
- /* destroy an USB endpoint */
- void fhci_ep0_free(struct fhci_usb *usb)
- {
- struct endpoint *ep;
- int size;
- ep = usb->ep0;
- if (ep) {
- if (ep->td_base)
- cpm_muram_free(cpm_muram_offset(ep->td_base));
- if (kfifo_initialized(&ep->conf_frame_Q)) {
- size = cq_howmany(&ep->conf_frame_Q);
- for (; size; size--) {
- struct packet *pkt = cq_get(&ep->conf_frame_Q);
- kfree(pkt);
- }
- cq_delete(&ep->conf_frame_Q);
- }
- if (kfifo_initialized(&ep->empty_frame_Q)) {
- size = cq_howmany(&ep->empty_frame_Q);
- for (; size; size--) {
- struct packet *pkt = cq_get(&ep->empty_frame_Q);
- kfree(pkt);
- }
- cq_delete(&ep->empty_frame_Q);
- }
- if (kfifo_initialized(&ep->dummy_packets_Q)) {
- size = cq_howmany(&ep->dummy_packets_Q);
- for (; size; size--) {
- u8 *buff = cq_get(&ep->dummy_packets_Q);
- kfree(buff);
- }
- cq_delete(&ep->dummy_packets_Q);
- }
- kfree(ep);
- usb->ep0 = NULL;
- }
- }
- /*
- * create the endpoint structure
- *
- * arguments:
- * usb A pointer to the data structure of the USB
- * data_mem The data memory partition(BUS)
- * ring_len TD ring length
- */
- u32 fhci_create_ep(struct fhci_usb *usb, enum fhci_mem_alloc data_mem,
- u32 ring_len)
- {
- struct endpoint *ep;
- struct usb_td __iomem *td;
- unsigned long ep_offset;
- char *err_for = "endpoint PRAM";
- int ep_mem_size;
- u32 i;
- /* we need at least 3 TDs in the ring */
- if (!(ring_len > 2)) {
- fhci_err(usb->fhci, "illegal TD ring length parameters\n");
- return -EINVAL;
- }
- ep = kzalloc(sizeof(*ep), GFP_KERNEL);
- if (!ep)
- return -ENOMEM;
- ep_mem_size = ring_len * sizeof(*td) + sizeof(struct fhci_ep_pram);
- ep_offset = cpm_muram_alloc(ep_mem_size, 32);
- if (IS_ERR_VALUE(ep_offset))
- goto err;
- ep->td_base = cpm_muram_addr(ep_offset);
- /* zero all queue pointers */
- if (cq_new(&ep->conf_frame_Q, ring_len + 2) ||
- cq_new(&ep->empty_frame_Q, ring_len + 2) ||
- cq_new(&ep->dummy_packets_Q, ring_len + 2)) {
- err_for = "frame_queues";
- goto err;
- }
- for (i = 0; i < (ring_len + 1); i++) {
- struct packet *pkt;
- u8 *buff;
- pkt = kmalloc(sizeof(*pkt), GFP_KERNEL);
- if (!pkt) {
- err_for = "frame";
- goto err;
- }
- buff = kmalloc_array(1028, sizeof(*buff), GFP_KERNEL);
- if (!buff) {
- kfree(pkt);
- err_for = "buffer";
- goto err;
- }
- cq_put(&ep->empty_frame_Q, pkt);
- cq_put(&ep->dummy_packets_Q, buff);
- }
- /* we put the endpoint parameter RAM right behind the TD ring */
- ep->ep_pram_ptr = (void __iomem *)ep->td_base + sizeof(*td) * ring_len;
- ep->conf_td = ep->td_base;
- ep->empty_td = ep->td_base;
- ep->already_pushed_dummy_bd = false;
- /* initialize tds */
- td = ep->td_base;
- for (i = 0; i < ring_len; i++) {
- out_be32(&td->buf_ptr, 0);
- out_be16(&td->status, 0);
- out_be16(&td->length, 0);
- out_be16(&td->extra, 0);
- td++;
- }
- td--;
- out_be16(&td->status, TD_W); /* for last TD set Wrap bit */
- out_be16(&td->length, 0);
- /* endpoint structure has been created */
- usb->ep0 = ep;
- return 0;
- err:
- fhci_ep0_free(usb);
- kfree(ep);
- fhci_err(usb->fhci, "no memory for the %s\n", err_for);
- return -ENOMEM;
- }
- /*
- * initialize the endpoint register according to the given parameters
- *
- * artuments:
- * usb A pointer to the data strucutre of the USB
- * ep A pointer to the endpoint structre
- * data_mem The data memory partition(BUS)
- */
- void fhci_init_ep_registers(struct fhci_usb *usb, struct endpoint *ep,
- enum fhci_mem_alloc data_mem)
- {
- u8 rt;
- /* set the endpoint registers according to the endpoint */
- out_be16(&usb->fhci->regs->usb_usep[0],
- USB_TRANS_CTR | USB_EP_MF | USB_EP_RTE);
- out_be16(&usb->fhci->pram->ep_ptr[0],
- cpm_muram_offset(ep->ep_pram_ptr));
- rt = (BUS_MODE_BO_BE | BUS_MODE_GBL);
- #ifdef MULTI_DATA_BUS
- if (data_mem == MEM_SECONDARY)
- rt |= BUS_MODE_DTB;
- #endif
- out_8(&ep->ep_pram_ptr->rx_func_code, rt);
- out_8(&ep->ep_pram_ptr->tx_func_code, rt);
- out_be16(&ep->ep_pram_ptr->rx_buff_len, 1028);
- out_be16(&ep->ep_pram_ptr->rx_base, 0);
- out_be16(&ep->ep_pram_ptr->tx_base, cpm_muram_offset(ep->td_base));
- out_be16(&ep->ep_pram_ptr->rx_bd_ptr, 0);
- out_be16(&ep->ep_pram_ptr->tx_bd_ptr, cpm_muram_offset(ep->td_base));
- out_be32(&ep->ep_pram_ptr->tx_state, 0);
- }
- /*
- * Collect the submitted frames and inform the application about them
- * It is also preparing the TDs for new frames. If the Tx interrupts
- * are disabled, the application should call that routine to get
- * confirmation about the submitted frames. Otherwise, the routine is
- * called from the interrupt service routine during the Tx interrupt.
- * In that case the application is informed by calling the application
- * specific 'fhci_transaction_confirm' routine
- */
- static void fhci_td_transaction_confirm(struct fhci_usb *usb)
- {
- struct endpoint *ep = usb->ep0;
- struct packet *pkt;
- struct usb_td __iomem *td;
- u16 extra_data;
- u16 td_status;
- u16 td_length;
- u32 buf;
- /*
- * collect transmitted BDs from the chip. The routine clears all BDs
- * with R bit = 0 and the pointer to data buffer is not NULL, that is
- * BDs which point to the transmitted data buffer
- */
- while (1) {
- td = ep->conf_td;
- td_status = in_be16(&td->status);
- td_length = in_be16(&td->length);
- buf = in_be32(&td->buf_ptr);
- extra_data = in_be16(&td->extra);
- /* check if the TD is empty */
- if (!(!(td_status & TD_R) && ((td_status & ~TD_W) || buf)))
- break;
- /* check if it is a dummy buffer */
- else if ((buf == DUMMY_BD_BUFFER) && !(td_status & ~TD_W))
- break;
- /* mark TD as empty */
- clrbits16(&td->status, ~TD_W);
- out_be16(&td->length, 0);
- out_be32(&td->buf_ptr, 0);
- out_be16(&td->extra, 0);
- /* advance the TD pointer */
- ep->conf_td = next_bd(ep->td_base, ep->conf_td, td_status);
- /* check if it is a dummy buffer(type2) */
- if ((buf == DUMMY2_BD_BUFFER) && !(td_status & ~TD_W))
- continue;
- pkt = cq_get(&ep->conf_frame_Q);
- if (!pkt)
- fhci_err(usb->fhci, "no frame to confirm\n");
- if (td_status & TD_ERRORS) {
- if (td_status & TD_RXER) {
- if (td_status & TD_CR)
- pkt->status = USB_TD_RX_ER_CRC;
- else if (td_status & TD_AB)
- pkt->status = USB_TD_RX_ER_BITSTUFF;
- else if (td_status & TD_OV)
- pkt->status = USB_TD_RX_ER_OVERUN;
- else if (td_status & TD_BOV)
- pkt->status = USB_TD_RX_DATA_OVERUN;
- else if (td_status & TD_NO)
- pkt->status = USB_TD_RX_ER_NONOCT;
- else
- fhci_err(usb->fhci, "illegal error "
- "occurred\n");
- } else if (td_status & TD_NAK)
- pkt->status = USB_TD_TX_ER_NAK;
- else if (td_status & TD_TO)
- pkt->status = USB_TD_TX_ER_TIMEOUT;
- else if (td_status & TD_UN)
- pkt->status = USB_TD_TX_ER_UNDERUN;
- else if (td_status & TD_STAL)
- pkt->status = USB_TD_TX_ER_STALL;
- else
- fhci_err(usb->fhci, "illegal error occurred\n");
- } else if ((extra_data & TD_TOK_IN) &&
- pkt->len > td_length - CRC_SIZE) {
- pkt->status = USB_TD_RX_DATA_UNDERUN;
- }
- if (extra_data & TD_TOK_IN)
- pkt->len = td_length - CRC_SIZE;
- else if (pkt->info & PKT_ZLP)
- pkt->len = 0;
- else
- pkt->len = td_length;
- fhci_transaction_confirm(usb, pkt);
- }
- }
- /*
- * Submitting a data frame to a specified endpoint of a USB device
- * The frame is put in the driver's transmit queue for this endpoint
- *
- * Arguments:
- * usb A pointer to the USB structure
- * pkt A pointer to the user frame structure
- * trans_type Transaction tyep - IN,OUT or SETUP
- * dest_addr Device address - 0~127
- * dest_ep Endpoint number of the device - 0~16
- * trans_mode Pipe type - ISO,Interrupt,bulk or control
- * dest_speed USB speed - Low speed or FULL speed
- * data_toggle Data sequence toggle - 0 or 1
- */
- u32 fhci_host_transaction(struct fhci_usb *usb,
- struct packet *pkt,
- enum fhci_ta_type trans_type,
- u8 dest_addr,
- u8 dest_ep,
- enum fhci_tf_mode trans_mode,
- enum fhci_speed dest_speed, u8 data_toggle)
- {
- struct endpoint *ep = usb->ep0;
- struct usb_td __iomem *td;
- u16 extra_data;
- u16 td_status;
- fhci_usb_disable_interrupt(usb);
- /* start from the next BD that should be filled */
- td = ep->empty_td;
- td_status = in_be16(&td->status);
- if (td_status & TD_R && in_be16(&td->length)) {
- /* if the TD is not free */
- fhci_usb_enable_interrupt(usb);
- return -1;
- }
- /* get the next TD in the ring */
- ep->empty_td = next_bd(ep->td_base, ep->empty_td, td_status);
- fhci_usb_enable_interrupt(usb);
- pkt->priv_data = td;
- out_be32(&td->buf_ptr, virt_to_phys(pkt->data));
- /* sets up transaction parameters - addr,endp,dir,and type */
- extra_data = (dest_ep << TD_ENDP_SHIFT) | dest_addr;
- switch (trans_type) {
- case FHCI_TA_IN:
- extra_data |= TD_TOK_IN;
- break;
- case FHCI_TA_OUT:
- extra_data |= TD_TOK_OUT;
- break;
- case FHCI_TA_SETUP:
- extra_data |= TD_TOK_SETUP;
- break;
- }
- if (trans_mode == FHCI_TF_ISO)
- extra_data |= TD_ISO;
- out_be16(&td->extra, extra_data);
- /* sets up the buffer descriptor */
- td_status = ((td_status & TD_W) | TD_R | TD_L | TD_I | TD_CNF);
- if (!(pkt->info & PKT_NO_CRC))
- td_status |= TD_TC;
- switch (trans_type) {
- case FHCI_TA_IN:
- if (data_toggle)
- pkt->info |= PKT_PID_DATA1;
- else
- pkt->info |= PKT_PID_DATA0;
- break;
- default:
- if (data_toggle) {
- td_status |= TD_PID_DATA1;
- pkt->info |= PKT_PID_DATA1;
- } else {
- td_status |= TD_PID_DATA0;
- pkt->info |= PKT_PID_DATA0;
- }
- break;
- }
- if ((dest_speed == FHCI_LOW_SPEED) &&
- (usb->port_status == FHCI_PORT_FULL))
- td_status |= TD_LSP;
- out_be16(&td->status, td_status);
- /* set up buffer length */
- if (trans_type == FHCI_TA_IN)
- out_be16(&td->length, pkt->len + CRC_SIZE);
- else
- out_be16(&td->length, pkt->len);
- /* put the frame to the confirmation queue */
- cq_put(&ep->conf_frame_Q, pkt);
- if (cq_howmany(&ep->conf_frame_Q) == 1)
- out_8(&usb->fhci->regs->usb_uscom, USB_CMD_STR_FIFO);
- return 0;
- }
- /* Reset the Tx BD ring */
- void fhci_flush_bds(struct fhci_usb *usb)
- {
- u16 extra_data;
- u16 td_status;
- u32 buf;
- struct usb_td __iomem *td;
- struct endpoint *ep = usb->ep0;
- td = ep->td_base;
- while (1) {
- td_status = in_be16(&td->status);
- buf = in_be32(&td->buf_ptr);
- extra_data = in_be16(&td->extra);
- /* if the TD is not empty - we'll confirm it as Timeout */
- if (td_status & TD_R)
- out_be16(&td->status, (td_status & ~TD_R) | TD_TO);
- /* if this TD is dummy - let's skip this TD */
- else if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER)
- out_be32(&td->buf_ptr, DUMMY2_BD_BUFFER);
- /* if this is the last TD - break */
- if (td_status & TD_W)
- break;
- td++;
- }
- fhci_td_transaction_confirm(usb);
- td = ep->td_base;
- do {
- out_be16(&td->status, 0);
- out_be16(&td->length, 0);
- out_be32(&td->buf_ptr, 0);
- out_be16(&td->extra, 0);
- td++;
- } while (!(in_be16(&td->status) & TD_W));
- out_be16(&td->status, TD_W); /* for last TD set Wrap bit */
- out_be16(&td->length, 0);
- out_be32(&td->buf_ptr, 0);
- out_be16(&td->extra, 0);
- out_be16(&ep->ep_pram_ptr->tx_bd_ptr,
- in_be16(&ep->ep_pram_ptr->tx_base));
- out_be32(&ep->ep_pram_ptr->tx_state, 0);
- out_be16(&ep->ep_pram_ptr->tx_cnt, 0);
- ep->empty_td = ep->td_base;
- ep->conf_td = ep->td_base;
- }
- /*
- * Flush all transmitted packets from TDs in the actual frame.
- * This routine is called when something wrong with the controller and
- * we want to get rid of the actual frame and start again next frame
- */
- void fhci_flush_actual_frame(struct fhci_usb *usb)
- {
- u8 mode;
- u16 tb_ptr;
- u16 extra_data;
- u16 td_status;
- u32 buf_ptr;
- struct usb_td __iomem *td;
- struct endpoint *ep = usb->ep0;
- /* disable the USB controller */
- mode = in_8(&usb->fhci->regs->usb_usmod);
- out_8(&usb->fhci->regs->usb_usmod, mode & ~USB_MODE_EN);
- tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr);
- td = cpm_muram_addr(tb_ptr);
- td_status = in_be16(&td->status);
- buf_ptr = in_be32(&td->buf_ptr);
- extra_data = in_be16(&td->extra);
- do {
- if (td_status & TD_R) {
- out_be16(&td->status, (td_status & ~TD_R) | TD_TO);
- } else {
- out_be32(&td->buf_ptr, 0);
- ep->already_pushed_dummy_bd = false;
- break;
- }
- /* advance the TD pointer */
- td = next_bd(ep->td_base, td, td_status);
- td_status = in_be16(&td->status);
- buf_ptr = in_be32(&td->buf_ptr);
- extra_data = in_be16(&td->extra);
- } while ((td_status & TD_R) || buf_ptr);
- fhci_td_transaction_confirm(usb);
- out_be16(&ep->ep_pram_ptr->tx_bd_ptr,
- in_be16(&ep->ep_pram_ptr->tx_base));
- out_be32(&ep->ep_pram_ptr->tx_state, 0);
- out_be16(&ep->ep_pram_ptr->tx_cnt, 0);
- ep->empty_td = ep->td_base;
- ep->conf_td = ep->td_base;
- usb->actual_frame->frame_status = FRAME_TIMER_END_TRANSMISSION;
- /* reset the event register */
- out_be16(&usb->fhci->regs->usb_usber, 0xffff);
- /* enable the USB controller */
- out_8(&usb->fhci->regs->usb_usmod, mode | USB_MODE_EN);
- }
- /* handles Tx confirm and Tx error interrupt */
- void fhci_tx_conf_interrupt(struct fhci_usb *usb)
- {
- fhci_td_transaction_confirm(usb);
- /*
- * Schedule another transaction to this frame only if we have
- * already confirmed all transaction in the frame.
- */
- if (((fhci_get_sof_timer_count(usb) < usb->max_frame_usage) ||
- (usb->actual_frame->frame_status & FRAME_END_TRANSMISSION)) &&
- (list_empty(&usb->actual_frame->tds_list)))
- fhci_schedule_transactions(usb);
- }
- void fhci_host_transmit_actual_frame(struct fhci_usb *usb)
- {
- u16 tb_ptr;
- u16 td_status;
- struct usb_td __iomem *td;
- struct endpoint *ep = usb->ep0;
- tb_ptr = in_be16(&ep->ep_pram_ptr->tx_bd_ptr);
- td = cpm_muram_addr(tb_ptr);
- if (in_be32(&td->buf_ptr) == DUMMY_BD_BUFFER) {
- struct usb_td __iomem *old_td = td;
- ep->already_pushed_dummy_bd = false;
- td_status = in_be16(&td->status);
- /* gets the next TD in the ring */
- td = next_bd(ep->td_base, td, td_status);
- tb_ptr = cpm_muram_offset(td);
- out_be16(&ep->ep_pram_ptr->tx_bd_ptr, tb_ptr);
- /* start transmit only if we have something in the TDs */
- if (in_be16(&td->status) & TD_R)
- out_8(&usb->fhci->regs->usb_uscom, USB_CMD_STR_FIFO);
- if (in_be32(&ep->conf_td->buf_ptr) == DUMMY_BD_BUFFER) {
- out_be32(&old_td->buf_ptr, 0);
- ep->conf_td = next_bd(ep->td_base, ep->conf_td,
- td_status);
- } else {
- out_be32(&old_td->buf_ptr, DUMMY2_BD_BUFFER);
- }
- }
- }
|