/* Number of endpoint */ #define PCH_UDC_EP_NUM 32 /* Total number of EPs (16 IN,16 OUT) */ #define PCH_UDC_USED_EP_NUM 4 /* EP number of EP's really used */ /* Length Value */ #define PCH_UDC_BRLEN 0x0F /* Burst length */ #define PCH_UDC_THLEN 0x1F /* Threshold length */ /* Value of EP Buffer Size */ #define UDC_EP0IN_BUFF_SIZE 16 #define UDC_EPIN_BUFF_SIZE 256 #define UDC_EP0OUT_BUFF_SIZE 16 #define UDC_EPOUT_BUFF_SIZE 256 /* Value of EP maximum packet size */ #define UDC_EP0IN_MAX_PKT_SIZE 64 #define UDC_EP0OUT_MAX_PKT_SIZE 64 #define UDC_BULK_MAX_PKT_SIZE 512
/* DMA */ #define DMA_DIR_RX 1 /* DMA for data receive */ #define DMA_DIR_TX 2 /* DMA for data transmit */ #define DMA_ADDR_INVALID (~(dma_addr_t)0) #define UDC_DMA_MAXPACKET 65536 /* maximum packet size for DMA */
/** * struct pch_udc_data_dma_desc - Structure to hold DMA descriptor information * for data * @status: Status quadlet * @reserved: Reserved * @dataptr: Buffer descriptor * @next: Next descriptor
*/ struct pch_udc_data_dma_desc {
u32 status;
u32 reserved;
u32 dataptr;
u32 next;
};
/** * struct pch_udc_stp_dma_desc - Structure to hold DMA descriptor information * for control data * @status: Status * @reserved: Reserved * @request: Control Request
*/ struct pch_udc_stp_dma_desc {
u32 status;
u32 reserved; struct usb_ctrlrequest request;
} __attribute((packed));
/* DMA status definitions */ /* Buffer status */ #define PCH_UDC_BUFF_STS 0xC0000000 #define PCH_UDC_BS_HST_RDY 0x00000000 #define PCH_UDC_BS_DMA_BSY 0x40000000 #define PCH_UDC_BS_DMA_DONE 0x80000000 #define PCH_UDC_BS_HST_BSY 0xC0000000 /* Rx/Tx Status */ #define PCH_UDC_RXTX_STS 0x30000000 #define PCH_UDC_RTS_SUCC 0x00000000 #define PCH_UDC_RTS_DESERR 0x10000000 #define PCH_UDC_RTS_BUFERR 0x30000000 /* Last Descriptor Indication */ #define PCH_UDC_DMA_LAST 0x08000000 /* Number of Rx/Tx Bytes Mask */ #define PCH_UDC_RXTX_BYTES 0x0000ffff
/** * struct pch_udc_cfg_data - Structure to hold current configuration * and interface information * @cur_cfg: current configuration in use * @cur_intf: current interface in use * @cur_alt: current alt interface in use
*/ struct pch_udc_cfg_data {
u16 cur_cfg;
u16 cur_intf;
u16 cur_alt;
};
/** * struct pch_udc_ep - Structure holding a PCH USB device Endpoint information * @ep: embedded ep request * @td_stp_phys: for setup request * @td_data_phys: for data request * @td_stp: for setup request * @td_data: for data request * @dev: reference to device struct * @offset_addr: offset address of ep register * @queue: queue for requests * @num: endpoint number * @in: endpoint is IN * @halted: endpoint halted? * @epsts: Endpoint status
*/ struct pch_udc_ep { struct usb_ep ep;
dma_addr_t td_stp_phys;
dma_addr_t td_data_phys; struct pch_udc_stp_dma_desc *td_stp; struct pch_udc_data_dma_desc *td_data; struct pch_udc_dev *dev; unsignedlong offset_addr; struct list_head queue; unsigned num:5,
in:1,
halted:1; unsignedlong epsts;
};
/** * struct pch_vbus_gpio_data - Structure holding GPIO informaton * for detecting VBUS * @port: gpio descriptor for the VBUS GPIO * @intr: gpio interrupt number * @irq_work_fall: Structure for WorkQueue * @irq_work_rise: Structure for WorkQueue
*/ struct pch_vbus_gpio_data { struct gpio_desc *port; int intr; struct work_struct irq_work_fall; struct work_struct irq_work_rise;
};
/** * struct pch_udc_dev - Structure holding complete information * of the PCH USB device * @gadget: gadget driver data * @driver: reference to gadget driver bound * @pdev: reference to the PCI device * @ep: array of endpoints * @lock: protects all state * @stall: stall requested * @prot_stall: protcol stall requested * @registered: driver registered with system * @suspended: driver in suspended state * @connected: gadget driver associated * @vbus_session: required vbus_session state * @set_cfg_not_acked: pending acknowledgement 4 setup * @waiting_zlp_ack: pending acknowledgement 4 ZLP * @data_requests: DMA pool for data requests * @stp_requests: DMA pool for setup requests * @dma_addr: DMA pool for received * @setup_data: Received setup data * @base_addr: for mapped device memory * @bar: PCI BAR used for mapped device memory * @cfg_data: current cfg, intf, and alt in use * @vbus_gpio: GPIO informaton for detecting VBUS
*/ struct pch_udc_dev { struct usb_gadget gadget; struct usb_gadget_driver *driver; struct pci_dev *pdev; struct pch_udc_ep ep[PCH_UDC_EP_NUM];
spinlock_t lock; /* protects all state */ unsigned
stall:1,
prot_stall:1,
suspended:1,
connected:1,
vbus_session:1,
set_cfg_not_acked:1,
waiting_zlp_ack:1; struct dma_pool *data_requests; struct dma_pool *stp_requests;
dma_addr_t dma_addr; struct usb_ctrlrequest setup_data; void __iomem *base_addr; unsignedshort bar; struct pch_udc_cfg_data cfg_data; struct pch_vbus_gpio_data vbus_gpio;
}; #define to_pch_udc(g) (container_of((g), struct pch_udc_dev, gadget))
/* Wait till idle */ while ((pch_udc_readl(dev, UDC_CSR_BUSY_ADDR) & UDC_CSR_BUSY)
&& --count)
cpu_relax(); if (!count)
dev_err(&dev->pdev->dev, "%s: wait error\n", __func__);
}
/** * pch_udc_write_csr() - Write the command and status registers. * @dev: Reference to pch_udc_dev structure * @val: value to be written to CSR register * @ep: end-point number
*/ staticvoid pch_udc_write_csr(struct pch_udc_dev *dev, unsignedlong val, unsignedint ep)
{ unsignedlong reg = PCH_UDC_CSR(ep);
pch_udc_csr_busy(dev); /* Wait till idle */
pch_udc_writel(dev, val, reg);
pch_udc_csr_busy(dev); /* Wait till idle */
}
/** * pch_udc_read_csr() - Read the command and status registers. * @dev: Reference to pch_udc_dev structure * @ep: end-point number * * Return codes: content of CSR register
*/ static u32 pch_udc_read_csr(struct pch_udc_dev *dev, unsignedint ep)
{ unsignedlong reg = PCH_UDC_CSR(ep);
/** * pch_udc_get_frame() - Get the current frame from device status register * @dev: Reference to pch_udc_dev structure * Retern current frame
*/ staticinlineint pch_udc_get_frame(struct pch_udc_dev *dev)
{
u32 frame = pch_udc_readl(dev, UDC_DEVSTS_ADDR); return (frame & UDC_DEVSTS_TS_MASK) >> UDC_DEVSTS_TS_SHIFT;
}
/** * pch_udc_clear_selfpowered() - Clear the self power control * @dev: Reference to pch_udc_regs structure
*/ staticinlinevoid pch_udc_clear_selfpowered(struct pch_udc_dev *dev)
{
pch_udc_bit_clr(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_SP);
}
/** * pch_udc_set_selfpowered() - Set the self power control * @dev: Reference to pch_udc_regs structure
*/ staticinlinevoid pch_udc_set_selfpowered(struct pch_udc_dev *dev)
{
pch_udc_bit_set(dev, UDC_DEVCFG_ADDR, UDC_DEVCFG_SP);
}
/** * pch_udc_set_disconnect() - Set the disconnect status. * @dev: Reference to pch_udc_regs structure
*/ staticinlinevoid pch_udc_set_disconnect(struct pch_udc_dev *dev)
{
pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_SD);
}
/** * pch_udc_reconnect() - This API initializes usb device controller, * and clear the disconnect status. * @dev: Reference to pch_udc_regs structure
*/ staticvoid pch_udc_reconnect(struct pch_udc_dev *dev)
{
pch_udc_init(dev);
/** * pch_udc_vbus_session() - set or clearr the disconnect status. * @dev: Reference to pch_udc_regs structure * @is_active: Parameter specifying the action * 0: indicating VBUS power is ending * !0: indicating VBUS power is starting
*/ staticinlinevoid pch_udc_vbus_session(struct pch_udc_dev *dev, int is_active)
{ unsignedlong iflags;
/** * pch_udc_ep_set_stall() - Set the stall of endpoint * @ep: Reference to structure of type pch_udc_ep_regs
*/ staticvoid pch_udc_ep_set_stall(struct pch_udc_ep *ep)
{ if (ep->in) {
pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_F);
pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_S);
} else {
pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_S);
}
}
/** * pch_udc_ep_clear_stall() - Clear the stall of endpoint * @ep: Reference to structure of type pch_udc_ep_regs
*/ staticinlinevoid pch_udc_ep_clear_stall(struct pch_udc_ep *ep)
{ /* Clear the stall */
pch_udc_ep_bit_clr(ep, UDC_EPCTL_ADDR, UDC_EPCTL_S); /* Clear NAK by writing CNAK */
pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_CNAK);
}
/** * pch_udc_ep_set_trfr_type() - Set the transfer type of endpoint * @ep: Reference to structure of type pch_udc_ep_regs * @type: Type of endpoint
*/ staticinlinevoid pch_udc_ep_set_trfr_type(struct pch_udc_ep *ep,
u8 type)
{
pch_udc_ep_writel(ep, ((type << UDC_EPCTL_ET_SHIFT) &
UDC_EPCTL_ET_MASK), UDC_EPCTL_ADDR);
}
/** * pch_udc_ep_set_bufsz() - Set the maximum packet size for the endpoint * @ep: Reference to structure of type pch_udc_ep_regs * @buf_size: The buffer word size * @ep_in: EP is IN
*/ staticvoid pch_udc_ep_set_bufsz(struct pch_udc_ep *ep,
u32 buf_size, u32 ep_in)
{
u32 data; if (ep_in) {
data = pch_udc_ep_readl(ep, UDC_BUFIN_FRAMENUM_ADDR);
data = (data & 0xffff0000) | (buf_size & 0xffff);
pch_udc_ep_writel(ep, data, UDC_BUFIN_FRAMENUM_ADDR);
} else {
data = pch_udc_ep_readl(ep, UDC_BUFOUT_MAXPKT_ADDR);
data = (buf_size << 16) | (data & 0xffff);
pch_udc_ep_writel(ep, data, UDC_BUFOUT_MAXPKT_ADDR);
}
}
/** * pch_udc_ep_set_maxpkt() - Set the Max packet size for the endpoint * @ep: Reference to structure of type pch_udc_ep_regs * @pkt_size: The packet byte size
*/ staticvoid pch_udc_ep_set_maxpkt(struct pch_udc_ep *ep, u32 pkt_size)
{
u32 data = pch_udc_ep_readl(ep, UDC_BUFOUT_MAXPKT_ADDR);
data = (data & 0xffff0000) | (pkt_size & 0xffff);
pch_udc_ep_writel(ep, data, UDC_BUFOUT_MAXPKT_ADDR);
}
/** * pch_udc_ep_set_subptr() - Set the Setup buffer pointer for the endpoint * @ep: Reference to structure of type pch_udc_ep_regs * @addr: Address of the register
*/ staticinlinevoid pch_udc_ep_set_subptr(struct pch_udc_ep *ep, u32 addr)
{
pch_udc_ep_writel(ep, addr, UDC_SUBPTR_ADDR);
}
/** * pch_udc_ep_set_ddptr() - Set the Data descriptor pointer for the endpoint * @ep: Reference to structure of type pch_udc_ep_regs * @addr: Address of the register
*/ staticinlinevoid pch_udc_ep_set_ddptr(struct pch_udc_ep *ep, u32 addr)
{
pch_udc_ep_writel(ep, addr, UDC_DESPTR_ADDR);
}
/** * pch_udc_ep_set_pd() - Set the poll demand bit for the endpoint * @ep: Reference to structure of type pch_udc_ep_regs
*/ staticinlinevoid pch_udc_ep_set_pd(struct pch_udc_ep *ep)
{
pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_P);
}
/** * pch_udc_ep_set_rrdy() - Set the receive ready bit for the endpoint * @ep: Reference to structure of type pch_udc_ep_regs
*/ staticinlinevoid pch_udc_ep_set_rrdy(struct pch_udc_ep *ep)
{
pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_RRDY);
}
/** * pch_udc_ep_clear_rrdy() - Clear the receive ready bit for the endpoint * @ep: Reference to structure of type pch_udc_ep_regs
*/ staticinlinevoid pch_udc_ep_clear_rrdy(struct pch_udc_ep *ep)
{
pch_udc_ep_bit_clr(ep, UDC_EPCTL_ADDR, UDC_EPCTL_RRDY);
}
/** * pch_udc_set_dma() - Set the 'TDE' or RDE bit of device control * register depending on the direction specified * @dev: Reference to structure of type pch_udc_regs * @dir: whether Tx or Rx * DMA_DIR_RX: Receive * DMA_DIR_TX: Transmit
*/ staticinlinevoid pch_udc_set_dma(struct pch_udc_dev *dev, int dir)
{ if (dir == DMA_DIR_RX)
pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RDE); elseif (dir == DMA_DIR_TX)
pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_TDE);
}
/** * pch_udc_clear_dma() - Clear the 'TDE' or RDE bit of device control * register depending on the direction specified * @dev: Reference to structure of type pch_udc_regs * @dir: Whether Tx or Rx * DMA_DIR_RX: Receive * DMA_DIR_TX: Transmit
*/ staticinlinevoid pch_udc_clear_dma(struct pch_udc_dev *dev, int dir)
{ if (dir == DMA_DIR_RX)
pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_RDE); elseif (dir == DMA_DIR_TX)
pch_udc_bit_clr(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_TDE);
}
/** * pch_udc_set_csr_done() - Set the device control register * CSR done field (bit 13) * @dev: reference to structure of type pch_udc_regs
*/ staticinlinevoid pch_udc_set_csr_done(struct pch_udc_dev *dev)
{
pch_udc_bit_set(dev, UDC_DEVCTL_ADDR, UDC_DEVCTL_CSR_DONE);
}
/** * pch_udc_disable_interrupts() - Disables the specified interrupts * @dev: Reference to structure of type pch_udc_regs * @mask: Mask to disable interrupts
*/ staticinlinevoid pch_udc_disable_interrupts(struct pch_udc_dev *dev,
u32 mask)
{
pch_udc_bit_set(dev, UDC_DEVIRQMSK_ADDR, mask);
}
/** * pch_udc_enable_interrupts() - Enable the specified interrupts * @dev: Reference to structure of type pch_udc_regs * @mask: Mask to enable interrupts
*/ staticinlinevoid pch_udc_enable_interrupts(struct pch_udc_dev *dev,
u32 mask)
{
pch_udc_bit_clr(dev, UDC_DEVIRQMSK_ADDR, mask);
}
/** * pch_udc_disable_ep_interrupts() - Disable endpoint interrupts * @dev: Reference to structure of type pch_udc_regs * @mask: Mask to disable interrupts
*/ staticinlinevoid pch_udc_disable_ep_interrupts(struct pch_udc_dev *dev,
u32 mask)
{
pch_udc_bit_set(dev, UDC_EPIRQMSK_ADDR, mask);
}
/** * pch_udc_enable_ep_interrupts() - Enable endpoint interrupts * @dev: Reference to structure of type pch_udc_regs * @mask: Mask to enable interrupts
*/ staticinlinevoid pch_udc_enable_ep_interrupts(struct pch_udc_dev *dev,
u32 mask)
{
pch_udc_bit_clr(dev, UDC_EPIRQMSK_ADDR, mask);
}
/** * pch_udc_read_device_interrupts() - Read the device interrupts * @dev: Reference to structure of type pch_udc_regs * Retern The device interrupts
*/ staticinline u32 pch_udc_read_device_interrupts(struct pch_udc_dev *dev)
{ return pch_udc_readl(dev, UDC_DEVIRQSTS_ADDR);
}
/** * pch_udc_write_device_interrupts() - Write device interrupts * @dev: Reference to structure of type pch_udc_regs * @val: The value to be written to interrupt register
*/ staticinlinevoid pch_udc_write_device_interrupts(struct pch_udc_dev *dev,
u32 val)
{
pch_udc_writel(dev, val, UDC_DEVIRQSTS_ADDR);
}
/** * pch_udc_read_ep_interrupts() - Read the endpoint interrupts * @dev: Reference to structure of type pch_udc_regs * Retern The endpoint interrupt
*/ staticinline u32 pch_udc_read_ep_interrupts(struct pch_udc_dev *dev)
{ return pch_udc_readl(dev, UDC_EPIRQSTS_ADDR);
}
/** * pch_udc_write_ep_interrupts() - Clear endpoint interupts * @dev: Reference to structure of type pch_udc_regs * @val: The value to be written to interrupt register
*/ staticinlinevoid pch_udc_write_ep_interrupts(struct pch_udc_dev *dev,
u32 val)
{
pch_udc_writel(dev, val, UDC_EPIRQSTS_ADDR);
}
/** * pch_udc_read_device_status() - Read the device status * @dev: Reference to structure of type pch_udc_regs * Retern The device status
*/ staticinline u32 pch_udc_read_device_status(struct pch_udc_dev *dev)
{ return pch_udc_readl(dev, UDC_DEVSTS_ADDR);
}
/** * pch_udc_read_ep_control() - Read the endpoint control * @ep: Reference to structure of type pch_udc_ep_regs * Retern The endpoint control register value
*/ staticinline u32 pch_udc_read_ep_control(struct pch_udc_ep *ep)
{ return pch_udc_ep_readl(ep, UDC_EPCTL_ADDR);
}
/** * pch_udc_clear_ep_control() - Clear the endpoint control register * @ep: Reference to structure of type pch_udc_ep_regs * Retern The endpoint control register value
*/ staticinlinevoid pch_udc_clear_ep_control(struct pch_udc_ep *ep)
{ return pch_udc_ep_writel(ep, 0, UDC_EPCTL_ADDR);
}
/** * pch_udc_read_ep_status() - Read the endpoint status * @ep: Reference to structure of type pch_udc_ep_regs * Retern The endpoint status
*/ staticinline u32 pch_udc_read_ep_status(struct pch_udc_ep *ep)
{ return pch_udc_ep_readl(ep, UDC_EPSTS_ADDR);
}
/** * pch_udc_clear_ep_status() - Clear the endpoint status * @ep: Reference to structure of type pch_udc_ep_regs * @stat: Endpoint status
*/ staticinlinevoid pch_udc_clear_ep_status(struct pch_udc_ep *ep,
u32 stat)
{ return pch_udc_ep_writel(ep, stat, UDC_EPSTS_ADDR);
}
/** * pch_udc_ep_set_nak() - Set the bit 7 (SNAK field) * of the endpoint control register * @ep: Reference to structure of type pch_udc_ep_regs
*/ staticinlinevoid pch_udc_ep_set_nak(struct pch_udc_ep *ep)
{
pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_SNAK);
}
/** * pch_udc_ep_clear_nak() - Set the bit 8 (CNAK field) * of the endpoint control register * @ep: reference to structure of type pch_udc_ep_regs
*/ staticvoid pch_udc_ep_clear_nak(struct pch_udc_ep *ep)
{ unsignedint loopcnt = 0; struct pch_udc_dev *dev = ep->dev;
if (!(pch_udc_ep_readl(ep, UDC_EPCTL_ADDR) & UDC_EPCTL_NAK)) return; if (!ep->in) {
loopcnt = 10000; while (!(pch_udc_read_ep_status(ep) & UDC_EPSTS_MRXFIFO_EMP) &&
--loopcnt)
udelay(5); if (!loopcnt)
dev_err(&dev->pdev->dev, "%s: RxFIFO not Empty\n",
__func__);
}
loopcnt = 10000; while ((pch_udc_read_ep_control(ep) & UDC_EPCTL_NAK) && --loopcnt) {
pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_CNAK);
udelay(5);
} if (!loopcnt)
dev_err(&dev->pdev->dev, "%s: Clear NAK not set for ep%d%s\n",
__func__, ep->num, (ep->in ? "in" : "out"));
}
/** * pch_udc_ep_fifo_flush() - Flush the endpoint fifo * @ep: reference to structure of type pch_udc_ep_regs * @dir: direction of endpoint * 0: endpoint is OUT * !0: endpoint is IN
*/ staticvoid pch_udc_ep_fifo_flush(struct pch_udc_ep *ep, int dir)
{ if (dir) { /* IN ep */
pch_udc_ep_bit_set(ep, UDC_EPCTL_ADDR, UDC_EPCTL_F); return;
}
}
/** * pch_udc_ep_enable() - This api enables endpoint * @ep: reference to structure of type pch_udc_ep_regs * @cfg: current configuration information * @desc: endpoint descriptor
*/ staticvoid pch_udc_ep_enable(struct pch_udc_ep *ep, struct pch_udc_cfg_data *cfg, conststruct usb_endpoint_descriptor *desc)
{
u32 val = 0;
u32 buff_size = 0;
if (ep->in)
pch_udc_write_csr(ep->dev, val, UDC_EPIN_IDX(ep->num)); else
pch_udc_write_csr(ep->dev, val, UDC_EPOUT_IDX(ep->num));
}
/** * pch_udc_ep_disable() - This api disables endpoint * @ep: reference to structure of type pch_udc_ep_regs
*/ staticvoid pch_udc_ep_disable(struct pch_udc_ep *ep)
{ if (ep->in) { /* flush the fifo */
pch_udc_ep_writel(ep, UDC_EPCTL_F, UDC_EPCTL_ADDR); /* set NAK */
pch_udc_ep_writel(ep, UDC_EPCTL_SNAK, UDC_EPCTL_ADDR);
pch_udc_ep_bit_set(ep, UDC_EPSTS_ADDR, UDC_EPSTS_IN);
} else { /* set NAK */
pch_udc_ep_writel(ep, UDC_EPCTL_SNAK, UDC_EPCTL_ADDR);
} /* reset desc pointer */
pch_udc_ep_writel(ep, 0, UDC_DESPTR_ADDR);
}
/** * pch_udc_wait_ep_stall() - Wait EP stall. * @ep: reference to structure of type pch_udc_ep_regs
*/ staticvoid pch_udc_wait_ep_stall(struct pch_udc_ep *ep)
{ unsignedint count = 10000;
/* Wait till idle */ while ((pch_udc_read_ep_control(ep) & UDC_EPCTL_S) && --count)
udelay(5); if (!count)
dev_err(&ep->dev->pdev->dev, "%s: wait error\n", __func__);
}
/** * pch_udc_init() - This API initializes usb device controller * @dev: Rreference to pch_udc_regs structure
*/ staticvoid pch_udc_init(struct pch_udc_dev *dev)
{ if (NULL == dev) {
pr_err("%s: Invalid address\n", __func__); return;
} /* Soft Reset and Reset PHY */
pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR);
pch_udc_writel(dev, UDC_SRST | UDC_PSRST, UDC_SRST_ADDR);
mdelay(1);
pch_udc_writel(dev, UDC_SRST, UDC_SRST_ADDR);
pch_udc_writel(dev, 0x00, UDC_SRST_ADDR);
mdelay(1); /* mask and clear all device interrupts */
pch_udc_bit_set(dev, UDC_DEVIRQMSK_ADDR, UDC_DEVINT_MSK);
pch_udc_bit_set(dev, UDC_DEVIRQSTS_ADDR, UDC_DEVINT_MSK);
/* mask and clear all ep interrupts */
pch_udc_bit_set(dev, UDC_EPIRQMSK_ADDR, UDC_EPINT_MSK_DISABLE_ALL);
pch_udc_bit_set(dev, UDC_EPIRQSTS_ADDR, UDC_EPINT_MSK_DISABLE_ALL);
/** * pch_udc_exit() - This API exit usb device controller * @dev: Reference to pch_udc_regs structure
*/ staticvoid pch_udc_exit(struct pch_udc_dev *dev)
{ /* mask all device interrupts */
pch_udc_bit_set(dev, UDC_DEVIRQMSK_ADDR, UDC_DEVINT_MSK); /* mask all ep interrupts */
pch_udc_bit_set(dev, UDC_EPIRQMSK_ADDR, UDC_EPINT_MSK_DISABLE_ALL); /* put device in disconnected state */
pch_udc_set_disconnect(dev);
}
/** * pch_udc_pcd_get_frame() - This API is invoked to get the current frame number * @gadget: Reference to the gadget driver * * Return codes: * 0: Success * -EINVAL: If the gadget passed is NULL
*/ staticint pch_udc_pcd_get_frame(struct usb_gadget *gadget)
{ struct pch_udc_dev *dev;
if (!gadget) return -EINVAL;
dev = container_of(gadget, struct pch_udc_dev, gadget); return pch_udc_get_frame(dev);
}
/** * pch_udc_pcd_wakeup() - This API is invoked to initiate a remote wakeup * @gadget: Reference to the gadget driver * * Return codes: * 0: Success * -EINVAL: If the gadget passed is NULL
*/ staticint pch_udc_pcd_wakeup(struct usb_gadget *gadget)
{ struct pch_udc_dev *dev; unsignedlong flags;
if (!gadget) return -EINVAL;
dev = container_of(gadget, struct pch_udc_dev, gadget);
spin_lock_irqsave(&dev->lock, flags);
pch_udc_rmt_wakeup(dev);
spin_unlock_irqrestore(&dev->lock, flags); return 0;
}
/** * pch_udc_pcd_selfpowered() - This API is invoked to specify whether the device * is self powered or not * @gadget: Reference to the gadget driver * @value: Specifies self powered or not * * Return codes: * 0: Success * -EINVAL: If the gadget passed is NULL
*/ staticint pch_udc_pcd_selfpowered(struct usb_gadget *gadget, int value)
{ struct pch_udc_dev *dev;
if (!gadget) return -EINVAL;
gadget->is_selfpowered = (value != 0);
dev = container_of(gadget, struct pch_udc_dev, gadget); if (value)
pch_udc_set_selfpowered(dev); else
pch_udc_clear_selfpowered(dev); return 0;
}
/** * pch_udc_pcd_pullup() - This API is invoked to make the device * visible/invisible to the host * @gadget: Reference to the gadget driver * @is_on: Specifies whether the pull up is made active or inactive * * Return codes: * 0: Success * -EINVAL: If the gadget passed is NULL
*/ staticint pch_udc_pcd_pullup(struct usb_gadget *gadget, int is_on)
{ struct pch_udc_dev *dev; unsignedlong iflags;
if (!gadget) return -EINVAL;
dev = container_of(gadget, struct pch_udc_dev, gadget);
/** * pch_udc_pcd_vbus_session() - This API is used by a driver for an external * transceiver (or GPIO) that * detects a VBUS power session starting/ending * @gadget: Reference to the gadget driver * @is_active: specifies whether the session is starting or ending * * Return codes: * 0: Success * -EINVAL: If the gadget passed is NULL
*/ staticint pch_udc_pcd_vbus_session(struct usb_gadget *gadget, int is_active)
{ struct pch_udc_dev *dev;
if (!gadget) return -EINVAL;
dev = container_of(gadget, struct pch_udc_dev, gadget);
pch_udc_vbus_session(dev, is_active); return 0;
}
/** * pch_udc_pcd_vbus_draw() - This API is used by gadget drivers during * SET_CONFIGURATION calls to * specify how much power the device can consume * @gadget: Reference to the gadget driver * @mA: specifies the current limit in 2mA unit * * Return codes: * -EINVAL: If the gadget passed is NULL * -EOPNOTSUPP:
*/ staticint pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsignedint mA)
{ return -EOPNOTSUPP;
}
/** * pch_vbus_gpio_get_value() - This API gets value of GPIO port as VBUS status. * @dev: Reference to the driver structure * * Return value: * 1: VBUS is high * 0: VBUS is low * -1: It is not enable to detect VBUS using GPIO
*/ staticint pch_vbus_gpio_get_value(struct pch_udc_dev *dev)
{ int vbus = 0;
/** * pch_vbus_gpio_irq() - IRQ handler for GPIO interrupt for changing VBUS * @irq: Interrupt request number * @data: Reference to the device structure * * Return codes: * 0: Success * -EINVAL: GPIO port is invalid or can't be initialized.
*/ static irqreturn_t pch_vbus_gpio_irq(int irq, void *data)
{ struct pch_udc_dev *dev = (struct pch_udc_dev *)data;
if (!dev->vbus_gpio.port || !dev->vbus_gpio.intr) return IRQ_NONE;
if (pch_vbus_gpio_get_value(dev))
schedule_work(&dev->vbus_gpio.irq_work_rise); else
schedule_work(&dev->vbus_gpio.irq_work_fall);
return IRQ_HANDLED;
}
/** * pch_vbus_gpio_init() - This API initializes GPIO port detecting VBUS. * @dev: Reference to the driver structure * * Return codes: * 0: Success * -EINVAL: GPIO port is invalid or can't be initialized.
*/ staticint pch_vbus_gpio_init(struct pch_udc_dev *dev)
{ struct device *d = &dev->pdev->dev; int err; int irq_num = 0; struct gpio_desc *gpiod;
/* Retrieve the GPIO line from the USB gadget device */
gpiod = devm_gpiod_get_optional(d, NULL, GPIOD_IN); if (IS_ERR(gpiod)) return PTR_ERR(gpiod);
gpiod_set_consumer_name(gpiod, "pch_vbus");
/** * pch_vbus_gpio_free() - This API frees resources of GPIO port * @dev: Reference to the driver structure
*/ staticvoid pch_vbus_gpio_free(struct pch_udc_dev *dev)
{ if (dev->vbus_gpio.intr)
free_irq(dev->vbus_gpio.intr, dev);
}
/** * complete_req() - This API is invoked from the driver when processing * of a request is complete * @ep: Reference to the endpoint structure * @req: Reference to the request structure * @status: Indicates the success/failure of completion
*/ staticvoid complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req, int status)
__releases(&dev->lock)
__acquires(&dev->lock)
{ struct pch_udc_dev *dev; unsigned halted = ep->halted;
list_del_init(&req->queue);
/* set new status if pending */ if (req->req.status == -EINPROGRESS)
req->req.status = status; else
status = req->req.status;
dev = ep->dev;
usb_gadget_unmap_request(&dev->gadget, &req->req, ep->in);
ep->halted = 1;
spin_unlock(&dev->lock); if (!ep->in)
pch_udc_ep_clear_rrdy(ep);
usb_gadget_giveback_request(&ep->ep, &req->req);
spin_lock(&dev->lock);
ep->halted = halted;
}
/** * empty_req_queue() - This API empties the request queue of an endpoint * @ep: Reference to the endpoint structure
*/ staticvoid empty_req_queue(struct pch_udc_ep *ep)
{ struct pch_udc_request *req;
ep->halted = 1; while (!list_empty(&ep->queue)) {
req = list_entry(ep->queue.next, struct pch_udc_request, queue);
complete_req(ep, req, -ESHUTDOWN); /* Remove from list */
}
}
/** * pch_udc_free_dma_chain() - This function frees the DMA chain created * for the request * @dev: Reference to the driver structure * @req: Reference to the request to be freed * * Return codes: * 0: Success
*/ staticvoid pch_udc_free_dma_chain(struct pch_udc_dev *dev, struct pch_udc_request *req)
{ struct pch_udc_data_dma_desc *td = req->td_data; unsigned i = req->chain_len;
dma_addr_t addr2;
dma_addr_t addr = (dma_addr_t)td->next;
td->next = 0x00; for (; i > 1; --i) { /* do not free first desc., will be done by free for request */
td = phys_to_virt(addr);
addr2 = (dma_addr_t)td->next;
dma_pool_free(dev->data_requests, td, addr);
addr = addr2;
}
req->chain_len = 1;
}
/** * pch_udc_create_dma_chain() - This function creates or reinitializes * a DMA chain * @ep: Reference to the endpoint structure * @req: Reference to the request * @buf_len: The buffer length * @gfp_flags: Flags to be used while mapping the data buffer * * Return codes: * 0: success, * -ENOMEM: dma_pool_alloc invocation fails
*/ staticint pch_udc_create_dma_chain(struct pch_udc_ep *ep, struct pch_udc_request *req, unsignedlong buf_len,
gfp_t gfp_flags)
{ struct pch_udc_data_dma_desc *td = req->td_data, *last; unsignedlong bytes = req->req.length, i = 0;
dma_addr_t dma_addr; unsigned len = 1;
if (req->chain_len > 1)
pch_udc_free_dma_chain(ep->dev, req);
/** * prepare_dma() - This function creates and initializes the DMA chain * for the request * @ep: Reference to the endpoint structure * @req: Reference to the request * @gfp: Flag to be used while mapping the data buffer * * Return codes: * 0: Success * Other 0: linux error number on failure
*/ staticint prepare_dma(struct pch_udc_ep *ep, struct pch_udc_request *req,
gfp_t gfp)
{ int retval;
/* Allocate and create a DMA chain */
retval = pch_udc_create_dma_chain(ep, req, ep->ep.maxpacket, gfp); if (retval) {
pr_err("%s: could not create DMA chain:%d\n", __func__, retval); return retval;
} if (ep->in)
req->td_data->status = (req->td_data->status &
~PCH_UDC_BUFF_STS) | PCH_UDC_BS_HST_RDY; return 0;
}
/** * process_zlp() - This function process zero length packets * from the gadget driver * @ep: Reference to the endpoint structure * @req: Reference to the request
*/ staticvoid process_zlp(struct pch_udc_ep *ep, struct pch_udc_request *req)
{ struct pch_udc_dev *dev = ep->dev;
/* IN zlp's are handled by hardware */
complete_req(ep, req, 0);
/* if set_config or set_intf is waiting for ack by zlp * then set CSR_DONE
*/ if (dev->set_cfg_not_acked) {
pch_udc_set_csr_done(dev);
dev->set_cfg_not_acked = 0;
} /* setup command is ACK'ed now by zlp */ if (!dev->stall && dev->waiting_zlp_ack) {
pch_udc_ep_clear_nak(&(dev->ep[UDC_EP0IN_IDX]));
dev->waiting_zlp_ack = 0;
}
}
/** * pch_udc_start_rxrequest() - This function starts the receive requirement. * @ep: Reference to the endpoint structure * @req: Reference to the request structure
*/ staticvoid pch_udc_start_rxrequest(struct pch_udc_ep *ep, struct pch_udc_request *req)
{ struct pch_udc_data_dma_desc *td_data;
pch_udc_clear_dma(ep->dev, DMA_DIR_RX);
td_data = req->td_data; /* Set the status bits for all descriptors */ while (1) {
td_data->status = (td_data->status & ~PCH_UDC_BUFF_STS) |
PCH_UDC_BS_HST_RDY; if ((td_data->status & PCH_UDC_DMA_LAST) == PCH_UDC_DMA_LAST) break;
td_data = phys_to_virt(td_data->next);
} /* Write the descriptor pointer */
pch_udc_ep_set_ddptr(ep, req->td_data_phys);
req->dma_going = 1;
pch_udc_enable_ep_interrupts(ep->dev, UDC_EPINT_OUT_EP0 << ep->num);
pch_udc_set_dma(ep->dev, DMA_DIR_RX);
pch_udc_ep_clear_nak(ep);
pch_udc_ep_set_rrdy(ep);
}
/** * pch_udc_pcd_ep_enable() - This API enables the endpoint. It is called * from gadget driver * @usbep: Reference to the USB endpoint structure * @desc: Reference to the USB endpoint descriptor structure * * Return codes: * 0: Success * -EINVAL: * -ESHUTDOWN:
*/ staticint pch_udc_pcd_ep_enable(struct usb_ep *usbep, conststruct usb_endpoint_descriptor *desc)
{ struct pch_udc_ep *ep; struct pch_udc_dev *dev; unsignedlong iflags;
/** * pch_udc_pcd_ep_disable() - This API disables endpoint and is called * from gadget driver * @usbep: Reference to the USB endpoint structure * * Return codes: * 0: Success * -EINVAL:
*/ staticint pch_udc_pcd_ep_disable(struct usb_ep *usbep)
{ struct pch_udc_ep *ep; unsignedlong iflags;
if (!usbep) return -EINVAL;
ep = container_of(usbep, struct pch_udc_ep, ep); if ((usbep->name == ep0_string) || !ep->ep.desc) return -EINVAL;
/** * pch_udc_alloc_request() - This function allocates request structure. * It is called by gadget driver * @usbep: Reference to the USB endpoint structure * @gfp: Flag to be used while allocating memory * * Return codes: * NULL: Failure * Allocated address: Success
*/ staticstruct usb_request *pch_udc_alloc_request(struct usb_ep *usbep,
gfp_t gfp)
{ struct pch_udc_request *req; struct pch_udc_ep *ep; struct pch_udc_data_dma_desc *dma_desc;
if (!usbep) return NULL;
ep = container_of(usbep, struct pch_udc_ep, ep);
req = kzalloc(sizeof *req, gfp); if (!req) return NULL;
req->req.dma = DMA_ADDR_INVALID;
INIT_LIST_HEAD(&req->queue); if (!ep->dev->dma_addr) return &req->req; /* ep0 in requests are allocated from data pool here */
dma_desc = dma_pool_alloc(ep->dev->data_requests, gfp,
&req->td_data_phys); if (NULL == dma_desc) {
kfree(req); return NULL;
} /* prevent from using desc. - set HOST BUSY */
dma_desc->status |= PCH_UDC_BS_HST_BSY;
dma_desc->dataptr = lower_32_bits(DMA_ADDR_INVALID);
req->td_data = dma_desc;
req->td_data_last = dma_desc;
req->chain_len = 1; return &req->req;
}
/** * pch_udc_free_request() - This function frees request structure. * It is called by gadget driver * @usbep: Reference to the USB endpoint structure * @usbreq: Reference to the USB request
*/ staticvoid pch_udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq)
{ struct pch_udc_ep *ep; struct pch_udc_request *req; struct pch_udc_dev *dev;
if (!usbep || !usbreq) return;
ep = container_of(usbep, struct pch_udc_ep, ep);
req = container_of(usbreq, struct pch_udc_request, req);
dev = ep->dev; if (!list_empty(&req->queue))
dev_err(&dev->pdev->dev, "%s: %s req=0x%p queue not empty\n",
__func__, usbep->name, req); if (req->td_data != NULL) { if (req->chain_len > 1)
pch_udc_free_dma_chain(ep->dev, req);
dma_pool_free(ep->dev->data_requests, req->td_data,
req->td_data_phys);
}
kfree(req);
}
/** * pch_udc_pcd_queue() - This function queues a request packet. It is called * by gadget driver * @usbep: Reference to the USB endpoint structure * @usbreq: Reference to the USB request * @gfp: Flag to be used while mapping the data buffer * * Return codes: * 0: Success * linux error number: Failure
*/ staticint pch_udc_pcd_queue(struct usb_ep *usbep, struct usb_request *usbreq,
gfp_t gfp)
{ int retval = 0; struct pch_udc_ep *ep; struct pch_udc_dev *dev; struct pch_udc_request *req; unsignedlong iflags;
if (!usbep || !usbreq || !usbreq->complete || !usbreq->buf) return -EINVAL;
ep = container_of(usbep, struct pch_udc_ep, ep);
dev = ep->dev; if (!ep->ep.desc && ep->num) return -EINVAL;
req = container_of(usbreq, struct pch_udc_request, req); if (!list_empty(&req->queue)) return -EINVAL; if (!dev->driver || (dev->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN;
spin_lock_irqsave(&dev->lock, iflags); /* map the buffer for dma */
retval = usb_gadget_map_request(&dev->gadget, usbreq, ep->in); if (retval) goto probe_end; if (usbreq->length > 0) {
retval = prepare_dma(ep, req, GFP_ATOMIC); if (retval) goto probe_end;
}
usbreq->actual = 0;
usbreq->status = -EINPROGRESS;
req->dma_done = 0; if (list_empty(&ep->queue) && !ep->halted) { /* no pending transfer, so start this req */ if (!usbreq->length) {
process_zlp(ep, req);
retval = 0; goto probe_end;
} if (!ep->in) {
pch_udc_start_rxrequest(ep, req);
} else { /* * For IN trfr the descriptors will be programmed and * P bit will be set when * we get an IN token
*/
pch_udc_wait_ep_stall(ep);
pch_udc_ep_clear_nak(ep);
pch_udc_enable_ep_interrupts(ep->dev, (1 << ep->num));
}
} /* Now add this request to the ep's pending requests */ if (req != NULL)
list_add_tail(&req->queue, &ep->queue);
/** * pch_udc_pcd_dequeue() - This function de-queues a request packet. * It is called by gadget driver * @usbep: Reference to the USB endpoint structure * @usbreq: Reference to the USB request * * Return codes: * 0: Success * linux error number: Failure
*/ staticint pch_udc_pcd_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
{ struct pch_udc_ep *ep; struct pch_udc_request *req; unsignedlong flags; int ret = -EINVAL;
ep = container_of(usbep, struct pch_udc_ep, ep); if (!usbep || !usbreq || (!ep->ep.desc && ep->num)) return ret;
req = container_of(usbreq, struct pch_udc_request, req);
spin_lock_irqsave(&ep->dev->lock, flags); /* make sure it's still queued on this endpoint */
list_for_each_entry(req, &ep->queue, queue) { if (&req->req == usbreq) {
pch_udc_ep_set_nak(ep); if (!list_empty(&req->queue))
complete_req(ep, req, -ECONNRESET);
ret = 0; break;
}
}
spin_unlock_irqrestore(&ep->dev->lock, flags); return ret;
}
/** * pch_udc_pcd_set_halt() - This function Sets or clear the endpoint halt * feature * @usbep: Reference to the USB endpoint structure * @halt: Specifies whether to set or clear the feature * * Return codes: * 0: Success * linux error number: Failure
*/ staticint pch_udc_pcd_set_halt(struct usb_ep *usbep, int halt)
{ struct pch_udc_ep *ep; unsignedlong iflags; int ret;
if (!usbep) return -EINVAL;
ep = container_of(usbep, struct pch_udc_ep, ep); if (!ep->ep.desc && !ep->num) return -EINVAL; if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN;
spin_lock_irqsave(&udc_stall_spinlock, iflags); if (list_empty(&ep->queue)) { if (halt) { if (ep->num == PCH_UDC_EP0)
ep->dev->stall = 1;
pch_udc_ep_set_stall(ep);
pch_udc_enable_ep_interrupts(
ep->dev, PCH_UDC_EPINT(ep->in, ep->num));
} else {
pch_udc_ep_clear_stall(ep);
}
ret = 0;
} else {
ret = -EAGAIN;
}
spin_unlock_irqrestore(&udc_stall_spinlock, iflags); return ret;
}
/** * pch_udc_pcd_set_wedge() - This function Sets or clear the endpoint * halt feature * @usbep: Reference to the USB endpoint structure * * Return codes: * 0: Success * linux error number: Failure
*/ staticint pch_udc_pcd_set_wedge(struct usb_ep *usbep)
{ struct pch_udc_ep *ep; unsignedlong iflags; int ret;
if (!usbep) return -EINVAL;
ep = container_of(usbep, struct pch_udc_ep, ep); if (!ep->ep.desc && !ep->num) return -EINVAL; if (!ep->dev->driver || (ep->dev->gadget.speed == USB_SPEED_UNKNOWN)) return -ESHUTDOWN;
spin_lock_irqsave(&udc_stall_spinlock, iflags); if (!list_empty(&ep->queue)) {
ret = -EAGAIN;
} else { if (ep->num == PCH_UDC_EP0)
ep->dev->stall = 1;
pch_udc_ep_set_stall(ep);
pch_udc_enable_ep_interrupts(ep->dev,
PCH_UDC_EPINT(ep->in, ep->num));
ep->dev->prot_stall = 1;
ret = 0;
}
spin_unlock_irqrestore(&udc_stall_spinlock, iflags); return ret;
}
/** * pch_udc_pcd_fifo_flush() - This function Flush the FIFO of specified endpoint * @usbep: Reference to the USB endpoint structure
*/ staticvoid pch_udc_pcd_fifo_flush(struct usb_ep *usbep)
{ struct pch_udc_ep *ep;
if (!usbep) return;
ep = container_of(usbep, struct pch_udc_ep, ep); if (ep->ep.desc || !ep->num)
pch_udc_ep_fifo_flush(ep, ep->in);
}
/** * pch_udc_complete_transfer() - This function completes a transfer * @ep: Reference to the endpoint structure
*/ staticvoid pch_udc_complete_transfer(struct pch_udc_ep *ep)
{ struct pch_udc_request *req; struct pch_udc_dev *dev = ep->dev;
if (list_empty(&ep->queue)) return;
req = list_entry(ep->queue.next, struct pch_udc_request, queue); if ((req->td_data_last->status & PCH_UDC_BUFF_STS) !=
PCH_UDC_BS_DMA_DONE) return; if ((req->td_data_last->status & PCH_UDC_RXTX_STS) !=
PCH_UDC_RTS_SUCC) {
dev_err(&dev->pdev->dev, "Invalid RXTX status (0x%08x) " "epstatus=0x%08x\n",
(req->td_data_last->status & PCH_UDC_RXTX_STS),
(int)(ep->epsts)); return;
}
/** * pch_udc_complete_receiver() - This function completes a receiver * @ep: Reference to the endpoint structure
*/ staticvoid pch_udc_complete_receiver(struct pch_udc_ep *ep)
{ struct pch_udc_request *req; struct pch_udc_dev *dev = ep->dev; unsignedint count; struct pch_udc_data_dma_desc *td;
dma_addr_t addr;
if (list_empty(&ep->queue)) return; /* next request */
req = list_entry(ep->queue.next, struct pch_udc_request, queue);
pch_udc_clear_dma(ep->dev, DMA_DIR_RX);
pch_udc_ep_set_ddptr(ep, 0); if ((req->td_data_last->status & PCH_UDC_BUFF_STS) ==
PCH_UDC_BS_DMA_DONE)
td = req->td_data_last; else
td = req->td_data;
while (1) { if ((td->status & PCH_UDC_RXTX_STS) != PCH_UDC_RTS_SUCC) {
dev_err(&dev->pdev->dev, "Invalid RXTX status=0x%08x " "epstatus=0x%08x\n",
(req->td_data->status & PCH_UDC_RXTX_STS),
(int)(ep->epsts)); return;
} if ((td->status & PCH_UDC_BUFF_STS) == PCH_UDC_BS_DMA_DONE) if (td->status & PCH_UDC_DMA_LAST) {
count = td->status & PCH_UDC_RXTX_BYTES; break;
} if (td == req->td_data_last) {
dev_err(&dev->pdev->dev, "Not complete RX descriptor"); return;
}
addr = (dma_addr_t)td->next;
td = phys_to_virt(addr);
} /* on 64k packets the RXBYTES field is zero */ if (!count && (req->req.length == UDC_DMA_MAXPACKET))
count = UDC_DMA_MAXPACKET;
req->td_data->status |= PCH_UDC_DMA_LAST;
td->status |= PCH_UDC_BS_HST_BSY;
req->dma_going = 0;
req->req.actual = count;
complete_req(ep, req, 0); /* If there is a new/failed requests try that now */ if (!list_empty(&ep->queue)) {
req = list_entry(ep->queue.next, struct pch_udc_request, queue);
pch_udc_start_rxrequest(ep, req);
}
}
/** * pch_udc_svc_data_in() - This function process endpoint interrupts * for IN endpoints * @dev: Reference to the device structure * @ep_num: Endpoint that generated the interrupt
*/ staticvoid pch_udc_svc_data_in(struct pch_udc_dev *dev, int ep_num)
{
u32 epsts; struct pch_udc_ep *ep;
ep = &dev->ep[UDC_EPIN_IDX(ep_num)];
epsts = ep->epsts;
ep->epsts = 0;
if (!(epsts & (UDC_EPSTS_IN | UDC_EPSTS_BNA | UDC_EPSTS_HE |
UDC_EPSTS_TDC | UDC_EPSTS_RCS | UDC_EPSTS_TXEMPTY |
UDC_EPSTS_RSS | UDC_EPSTS_XFERDONE))) return; if ((epsts & UDC_EPSTS_BNA)) return; if (epsts & UDC_EPSTS_HE) return; if (epsts & UDC_EPSTS_RSS) {
pch_udc_ep_set_stall(ep);
pch_udc_enable_ep_interrupts(ep->dev,
PCH_UDC_EPINT(ep->in, ep->num));
} if (epsts & UDC_EPSTS_RCS) { if (!dev->prot_stall) {
pch_udc_ep_clear_stall(ep);
} else {
pch_udc_ep_set_stall(ep);
pch_udc_enable_ep_interrupts(ep->dev,
PCH_UDC_EPINT(ep->in, ep->num));
}
} if (epsts & UDC_EPSTS_TDC)
pch_udc_complete_transfer(ep); /* On IN interrupt, provide data if we have any */ if ((epsts & UDC_EPSTS_IN) && !(epsts & UDC_EPSTS_RSS) &&
!(epsts & UDC_EPSTS_TDC) && !(epsts & UDC_EPSTS_TXEMPTY))
pch_udc_start_next_txrequest(ep);
}
/** * pch_udc_svc_data_out() - Handles interrupts from OUT endpoint * @dev: Reference to the device structure * @ep_num: Endpoint that generated the interrupt
*/ staticvoid pch_udc_svc_data_out(struct pch_udc_dev *dev, int ep_num)
{
u32 epsts; struct pch_udc_ep *ep; struct pch_udc_request *req = NULL;
ep = &dev->ep[UDC_EPOUT_IDX(ep_num)];
epsts = ep->epsts;
ep->epsts = 0;
if ((epsts & UDC_EPSTS_BNA) && (!list_empty(&ep->queue))) { /* next request */
req = list_entry(ep->queue.next, struct pch_udc_request,
queue); if ((req->td_data_last->status & PCH_UDC_BUFF_STS) !=
PCH_UDC_BS_DMA_DONE) { if (!req->dma_going)
pch_udc_start_rxrequest(ep, req); return;
}
} if (epsts & UDC_EPSTS_HE) return; if (epsts & UDC_EPSTS_RSS) {
pch_udc_ep_set_stall(ep);
pch_udc_enable_ep_interrupts(ep->dev,
PCH_UDC_EPINT(ep->in, ep->num));
} if (epsts & UDC_EPSTS_RCS) { if (!dev->prot_stall) {
pch_udc_ep_clear_stall(ep);
} else {
pch_udc_ep_set_stall(ep);
pch_udc_enable_ep_interrupts(ep->dev,
PCH_UDC_EPINT(ep->in, ep->num));
}
} if (((epsts & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_SHIFT) ==
UDC_EPSTS_OUT_DATA) { if (ep->dev->prot_stall == 1) {
pch_udc_ep_set_stall(ep);
pch_udc_enable_ep_interrupts(ep->dev,
PCH_UDC_EPINT(ep->in, ep->num));
} else {
pch_udc_complete_receiver(ep);
}
} if (list_empty(&ep->queue))
pch_udc_set_dma(dev, DMA_DIR_RX);
}
staticint pch_udc_gadget_setup(struct pch_udc_dev *dev)
__must_hold(&dev->lock)
{ int rc;
/* In some cases we can get an interrupt before driver gets setup */ if (!dev->driver) return -ESHUTDOWN;
if (!(epsts & (UDC_EPSTS_IN | UDC_EPSTS_BNA | UDC_EPSTS_HE |
UDC_EPSTS_TDC | UDC_EPSTS_RCS | UDC_EPSTS_TXEMPTY |
UDC_EPSTS_XFERDONE))) return; if ((epsts & UDC_EPSTS_BNA)) return; if (epsts & UDC_EPSTS_HE) return; if ((epsts & UDC_EPSTS_TDC) && (!dev->stall)) {
pch_udc_complete_transfer(ep);
pch_udc_clear_dma(dev, DMA_DIR_RX);
ep_out->td_data->status = (ep_out->td_data->status &
~PCH_UDC_BUFF_STS) |
PCH_UDC_BS_HST_RDY;
pch_udc_ep_clear_nak(ep_out);
pch_udc_set_dma(dev, DMA_DIR_RX);
pch_udc_ep_set_rrdy(ep_out);
} /* On IN interrupt, provide data if we have any */ if ((epsts & UDC_EPSTS_IN) && !(epsts & UDC_EPSTS_TDC) &&
!(epsts & UDC_EPSTS_TXEMPTY))
pch_udc_start_next_txrequest(ep);
}
/** * pch_udc_svc_control_out() - Routine that handle Control * OUT endpoint interrupts * @dev: Reference to the device structure
*/ staticvoid pch_udc_svc_control_out(struct pch_udc_dev *dev)
__releases(&dev->lock)
__acquires(&dev->lock)
{
u32 stat; int setup_supported; struct pch_udc_ep *ep;
ep = &dev->ep[UDC_EP0OUT_IDX];
stat = ep->epsts;
ep->epsts = 0;
/* If setup data */ if (((stat & UDC_EPSTS_OUT_MASK) >> UDC_EPSTS_OUT_SHIFT) ==
UDC_EPSTS_OUT_SETUP) {
dev->stall = 0;
dev->ep[UDC_EP0IN_IDX].halted = 0;
dev->ep[UDC_EP0OUT_IDX].halted = 0;
dev->setup_data = ep->td_stp->request;
pch_udc_init_setup_buff(ep->td_stp);
pch_udc_clear_dma(dev, DMA_DIR_RX);
pch_udc_ep_fifo_flush(&(dev->ep[UDC_EP0IN_IDX]),
dev->ep[UDC_EP0IN_IDX].in); if ((dev->setup_data.bRequestType & USB_DIR_IN))
dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IDX].ep; else/* OUT */
dev->gadget.ep0 = &ep->ep; /* If Mass storage Reset */ if ((dev->setup_data.bRequestType == 0x21) &&
(dev->setup_data.bRequest == 0xFF))
dev->prot_stall = 0; /* call gadget with setup data received */
setup_supported = pch_udc_gadget_setup(dev);
if (dev->setup_data.bRequestType & USB_DIR_IN) {
ep->td_data->status = (ep->td_data->status &
~PCH_UDC_BUFF_STS) |
PCH_UDC_BS_HST_RDY;
pch_udc_ep_set_ddptr(ep, ep->td_data_phys);
} /* ep0 in returns data on IN phase */ if (setup_supported >= 0 && setup_supported <
UDC_EP0IN_MAX_PKT_SIZE) {
pch_udc_ep_clear_nak(&(dev->ep[UDC_EP0IN_IDX])); /* Gadget would have queued a request when
* we called the setup */ if (!(dev->setup_data.bRequestType & USB_DIR_IN)) {
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.54 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.