cregit-Linux how code gets into the kernel

Release 4.7 drivers/usb/host/fhci-tds.c

Directory: drivers/usb/host
/*
 * 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>
 *
 * 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.
 */

#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; }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov38100.00%1100.00%
Total38100.00%1100.00%


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; } }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov6798.53%150.00%
geyslan g. bemgeyslan g. bem11.47%150.00%
Total68100.00%2100.00%

/* 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; } }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov22196.09%266.67%
stefani seiboldstefani seibold93.91%133.33%
Total230100.00%3100.00%

/* * 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(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; }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov44395.89%133.33%
stefani seiboldstefani seibold183.90%133.33%
masanari iidamasanari iida10.22%133.33%
Total462100.00%3100.00%

/* * 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); }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov19499.49%150.00%
guilherme maciel ferreiraguilherme maciel ferreira10.51%150.00%
Total195100.00%2100.00%

/* * 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); } }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov46099.35%133.33%
lucas de marchilucas de marchi20.43%133.33%
stefani seiboldstefani seibold10.22%133.33%
Total463100.00%3100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov40399.26%133.33%
stefani seiboldstefani seibold20.49%133.33%
guilherme maciel ferreiraguilherme maciel ferreira10.25%133.33%
Total406100.00%3100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov309100.00%1100.00%
Total309100.00%1100.00%

/* * 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); }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov32598.78%150.00%
guilherme maciel ferreiraguilherme maciel ferreira41.22%150.00%
Total329100.00%2100.00%

/* 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); }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov58100.00%1100.00%
Total58100.00%1100.00%


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); } } }

Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov20399.51%150.00%
guilherme maciel ferreiraguilherme maciel ferreira10.49%150.00%
Total204100.00%2100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
anton vorontsovanton vorontsov291798.41%222.22%
stefani seiboldstefani seibold301.01%111.11%
guilherme maciel ferreiraguilherme maciel ferreira70.24%111.11%
lucas de marchilucas de marchi40.13%111.11%
tejun heotejun heo30.10%111.11%
masanari iidamasanari iida10.03%111.11%
eric lescoueteric lescouet10.03%111.11%
geyslan g. bemgeyslan g. bem10.03%111.11%
Total2964100.00%9100.00%
Directory: drivers/usb/host
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}